<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on 0xDEADBEEF</title><link>https://detunized.net/posts/</link><description>Recent content in Posts on 0xDEADBEEF</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 06 Jun 2019 00:00:00 +0000</lastBuildDate><atom:link href="https://detunized.net/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Telegram bot in Go: charts</title><link>https://detunized.net/posts/2019-06-06-telegram-bot-in-go-charts/</link><pubDate>Thu, 06 Jun 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-06-06-telegram-bot-in-go-charts/</guid><description>It&amp;rsquo;s been a long time since I wrote about the bot I&amp;rsquo;m building. Life happened. A lot of it. Now, when things are settling down, I might have again the time and the energy to program the bot some more and write about it in the process.
Last time the bot learned some text commands. Now it&amp;rsquo;s time to draw some pretty pictures. Today I&amp;rsquo;d like to add a couple of chart commands.</description><content type="html"><![CDATA[<p>It&rsquo;s been a long time since I wrote about the bot I&rsquo;m building. Life happened. A lot of it. Now, when things are settling down, I might have again the time and the energy to program the bot some more and write about it in the process.</p>
<p>Last time the bot learned some <a href="https://detunized.net/posts/2019-04-09-telegram-bot-in-go-speak-robot/">text commands</a>. Now it&rsquo;s time to draw some pretty pictures. Today I&rsquo;d like to add a couple of chart commands.</p>
<p>Using Telegram API it possible to send an image (as well as a video or any other document), it&rsquo;s not limited to just text messages. All I need is a way to generate an image and push it down the wire. After a quick search, I stumbled upon <a href="https://github.com/wcharczuk/go-chart">go-chart</a>. <code>go-chart</code> renders charts into in-memory PNG and SVG files and this is exactly what I need. Here&rsquo;s an example:</p>
<p><img src="https://i.imgur.com/h8Cs9ts.png" alt="go-chart"></p>
<p>I&rsquo;m not so interested in visualizing stock prices though. I have my own data to deal with. The first step would be to draw something simple. Last time I added the <code>/top</code> command that shows 10 most logged events. It would be good to see that as a bar chart as well. Please welcome the <code>/topChart</code> command.</p>
<p><img src="https://i.imgur.com/VLebmjz.png" alt="top"></p>
<p>The charts look a bit too small and ugly like this, but they blow up once you click on them. The code for this chart is pretty simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">context</span><span class="p">)</span> <span class="nf">topChart</span><span class="p">(</span><span class="nx">args</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">num</span> <span class="o">:=</span> <span class="nf">parseTopArgs</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Get values from the DB and convert
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">values</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="nx">chart</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">num</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">e</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">c</span><span class="p">.</span><span class="nf">getTopEvents</span><span class="p">(</span><span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">values</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nx">values</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">chart</span><span class="p">.</span><span class="nx">Value</span><span class="p">{</span><span class="nx">Label</span><span class="p">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">Value</span><span class="p">:</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">count</span><span class="p">)},</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Chart settings
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">response</span> <span class="o">:=</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">BarChart</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Title</span><span class="p">:</span>      <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;Top %d events&#34;</span><span class="p">,</span> <span class="nx">num</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="nx">TitleStyle</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nf">StyleShow</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Background</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">Style</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">Padding</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">Box</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">Top</span><span class="p">:</span> <span class="mi">40</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Width</span><span class="p">:</span>    <span class="nx">num</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Height</span><span class="p">:</span>   <span class="mi">512</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">BarWidth</span><span class="p">:</span> <span class="mi">80</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">XAxis</span><span class="p">:</span>    <span class="nx">chart</span><span class="p">.</span><span class="nf">StyleShow</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="nx">YAxis</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">YAxis</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">Style</span><span class="p">:</span>          <span class="nx">chart</span><span class="p">.</span><span class="nf">StyleShow</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="nx">ValueFormatter</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">IntValueFormatter</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">Range</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">chart</span><span class="p">.</span><span class="nx">ContinuousRange</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">Min</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nx">Max</span><span class="p">:</span> <span class="nx">values</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">Value</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Bars</span><span class="p">:</span> <span class="nx">values</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Render and send
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">c</span><span class="p">.</span><span class="nf">sendChart</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To get the values I use the following query:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="n">freq</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">events</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">WHERE</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">?</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">freq</span><span class="w"> </span><span class="k">DESC</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">10</span><span class="w">
</span></span></span></code></pre></div><p>And rendering the cart is trivial with <code>go-chart</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="nx">buffer</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">err</span> <span class="o">:=</span> <span class="nx">chartSettings</span><span class="p">.</span><span class="nf">Render</span><span class="p">(</span><span class="nx">chart</span><span class="p">.</span><span class="nx">PNG</span><span class="p">,</span> <span class="nx">buffer</span><span class="p">)</span>
</span></span></code></pre></div><p>Now the <code>buffer</code> holds the bytes for a PNG file. It could be saved to disk or sent over the wire via Telegram API like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="nx">image</span> <span class="o">:=</span> <span class="nx">tgbotapi</span><span class="p">.</span><span class="nx">FileBytes</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;chart.png&#34;</span><span class="p">,</span> <span class="nx">Bytes</span><span class="p">:</span> <span class="nx">buffer</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">()}</span>
</span></span><span class="line"><span class="cl"><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">bot</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">tgbotapi</span><span class="p">.</span><span class="nf">NewPhotoUpload</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">Chat</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span> <span class="nx">image</span><span class="p">))</span>
</span></span></code></pre></div><p>Easy-peasy.</p>
<p>Another idea is to use a bar chart to show daily activity for a specific event like this:</p>
<p><img src="https://i.imgur.com/dwgrnzh.png" alt="month"></p>
<p>Up close it looks like this:</p>
<p><img src="https://i.imgur.com/cmC4CPg.png" alt="month"></p>
<p>What I thought would be very cool is to draw a diagram similar to GitHub activity for a selected event. On GitHub it looks like this:</p>
<p><img src="https://i.imgur.com/YuG2X26.png" alt="gitbut"></p>
<p>Easy? Well, not really. I spent a bunch of time trying to replicate it. I tried to save myself some trouble at first and use the stacked bar chart, but it didn&rsquo;t work out. So I had to start from scratch, roll up the sleeves and do it myself. About five hundred lines later I have this:</p>
<p><img src="https://i.imgur.com/kn0KzOf.png" alt="year"></p>
<p>Close enough for my needs. There&rsquo;s quite a bit of code to be covered in this post. Maybe I make a separate post some time where I describe what it takes to draw a chart using <code>go-chart</code>. It is doable. The problem is testing. Especially if you want to support all kinds of different options, like configurable axis labels, legend, title, font sizes and so on. It&rsquo;s quite tedious. I declare: <strong>works on my machine!</strong> Ship it!</p>
<h2 id="whats-next">What&rsquo;s next</h2>
<p>The bot seems to have all the basic features. Sure, they are not very robust and not polished, but they kinda work. It&rsquo;s time to start using it. I need to figure out a way to deploy it somewhere and have it running. DevOps stuff. I wonder if I&rsquo;m gonna need a Kubernetes cluster for that.</p>
<p>If you&rsquo;re curious, the code is <a href="https://github.com/detunized/since-bot/tree/day-6">available on GitHub</a>. This version is tagged <code>day-6</code>.</p>
<p><em>Also published on <a href="https://dev.to/detunized/telegram-bot-in-go-charts-5d6f">DEV</a> and <a href="https://medium.com/@detunized/telegram-bot-in-go-charts-2226265b04cc">Medium</a></em></p>
]]></content></item><item><title>Don't make me hack your software</title><link>https://detunized.net/posts/2019-04-17-dont-make-me-hack-your-software/</link><pubDate>Wed, 17 Apr 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-04-17-dont-make-me-hack-your-software/</guid><description>I got a new corporate VPN tool the other day. It&amp;rsquo;s called Pulse Secure. It worked fine, thank you very much, no complains there. But then I tried to quit
and I got this
That pissed me off right then and there. Why would any remote admin tell me what to do on my local machine? Even if it&amp;rsquo;s a company machine. When I do not use the VPN or maybe I&amp;rsquo;m not even connected to any WiFi, why should I have this running?</description><content type="html"><![CDATA[<p>I got a new corporate VPN tool the other day. It&rsquo;s called <a href="https://www.pulsesecure.net/">Pulse Secure</a>. It worked fine, thank you very much, no complains there. But then I tried to quit</p>
<p><img src="https://i.imgur.com/Nf6gDQ1.png" alt="exit"></p>
<p>and I got this</p>
<p><img src="https://i.imgur.com/f8rI72s.png" alt="disallowed"></p>
<p>That pissed me off right then and there. Why would any remote admin tell me what to do on my local machine? Even if it&rsquo;s a company machine. When I do not use the VPN or maybe I&rsquo;m not even connected to any WiFi, why should I have this running? It&rsquo;s just silly to try to prevent me from doing something on the machine where I have the root access. When this gets pushed onto a developer&rsquo;s laptop (as opposed to an accountant or an HR clerk) it simply becomes a challenge.</p>
<p>It has no impact on the security of the machine or the corporate network when it&rsquo;s simply running in the background. This is not anti-virus software. It has an impact on the battery life and other resources like RAM and CPU though. And I need those. It&rsquo;s exactly this type of programs that don&rsquo;t exit, hang around and prevent me from running yet another Electron app on my laptop. Sometimes one is not enough, you know.</p>
<p>I guess I&rsquo;d have to get my hands dirty instead of doing something I was actually going to do. Simply killing it from the command line didn&rsquo;t work. It just starts over. Investigation it is, then.</p>
<p>First, I took a quick look at the list of the open files from the Activity Monitor. There I found a log file (bottom row):</p>
<p><img src="https://i.imgur.com/2g86MNQ.png" alt="am"></p>
<p>The tool developers were very kind and dumped about half a meg of stuff on every restart cycle into the log. So it&rsquo;s wasting my disk space as well then. Amongst thousands of lines, I found a reference to <code>/Library/Application Support/Pulse Secure/Pulse/connstore.dat</code>. It sounded promising. After poking around in that file and trying this and that, I found a parameter that is responsible for that.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">ive</span> <span class="s2">&#34;921sn438-qoo8-4pp4-85p7-68q5s2592o32&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="nx">connection</span><span class="o">-</span><span class="nx">policy</span><span class="o">-</span><span class="nx">override</span><span class="o">:</span> <span class="s2">&#34;false&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>When this parameter is changed to <code>true</code> and the tool is restarted I was able to quit. Voilà!</p>
<p><img src="https://i.imgur.com/xfEeC0C.png" alt="done"></p>
<p>Yes, I do.</p>
<p>And here the script that automates the whole thing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s1">&#39;/Library/Application Support/Pulse Secure/Pulse&#39;</span>
</span></span><span class="line"><span class="cl">sudo sed -i <span class="s1">&#39;&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      s<span class="s1">&#39;/connection-policy-override: &#34;false&#34;/connection-policy-override: &#34;true&#34;/&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      connstore.dat
</span></span><span class="line"><span class="cl">sudo killall PulseTray <span class="s1">&#39;Pulse Secure&#39;</span> dsAccessService
</span></span></code></pre></div><p>Dear developers and sysadmins, please don&rsquo;t do that again. You&rsquo;re just wasting everybody&rsquo;s time. Rather put your efforts into making the software more reliable, less resource hungry, more secure.</p>
<p><em>Also published on <a href="https://dev.to/detunized/don-t-make-me-hack-your-software-2k8d">DEV</a> and <a href="https://medium.com/@detunized/dont-make-me-hack-your-software-36116de3c4d2">Medium</a></em></p>
]]></content></item><item><title>Telegram bot in Go: speak robot</title><link>https://detunized.net/posts/2019-04-09-telegram-bot-in-go-speak-robot/</link><pubDate>Tue, 09 Apr 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-04-09-telegram-bot-in-go-speak-robot/</guid><description>Last time I taught my bot to speak human. This time I&amp;rsquo;m gonna teach it to speak robot. I&amp;rsquo;m going to add a few bot commands. In Telegram the bots receive text exactly as you send it. By convention though, when the first word starts with a backslash (/) it&amp;rsquo;s interpreted as a command. Commands are used to tell the bot what to do. It&amp;rsquo;s a bit like shell, where you spend, I assume, most of your time.</description><content type="html"><![CDATA[<p>Last time I taught my bot to <a href="https://detunized.net/posts/2019-04-04-telegram-bot-in-go-speak-human/">speak human</a>. This time I&rsquo;m gonna teach it to speak robot. I&rsquo;m going to add a few bot commands. In Telegram the bots receive text exactly as you send it. By convention though, when the first word starts with a backslash (<code>/</code>) it&rsquo;s interpreted as a command. Commands are used to tell the bot what to do. It&rsquo;s a bit like shell, where you spend, I assume, most of your time.</p>
<p>The <code>go-telegram-bot-api/telegram-bot-api</code> package provides some functions to check whether there&rsquo;s a command in the received message and lets you extract it and its arguments.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">update</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">updates</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">update</span><span class="p">.</span><span class="nx">Message</span><span class="p">.</span><span class="nf">IsCommand</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">command</span> <span class="o">:=</span> <span class="nx">update</span><span class="p">.</span><span class="nx">Message</span><span class="p">.</span><span class="nf">Command</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nx">arguments</span> <span class="o">:=</span> <span class="nx">update</span><span class="p">.</span><span class="nx">Message</span><span class="p">.</span><span class="nf">CommandArguments</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>After some light refactoring my bot is ready to accept commands as well as regular text. Actually, the plain text is simply mapped to the <code>/add</code> command.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">message</span><span class="p">.</span><span class="nf">IsCommand</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="nx">command</span> <span class="o">:=</span> <span class="nx">message</span><span class="p">.</span><span class="nf">Command</span><span class="p">();</span> <span class="nx">command</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;a&#34;</span><span class="p">,</span> <span class="s">&#34;add&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">c</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nf">CommandArguments</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;e&#34;</span><span class="p">,</span> <span class="s">&#34;export&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">c</span><span class="p">.</span><span class="nf">export</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;h&#34;</span><span class="p">,</span> <span class="s">&#34;help&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">c</span><span class="p">.</span><span class="nf">help</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;s&#34;</span><span class="p">,</span> <span class="s">&#34;since&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">c</span><span class="p">.</span><span class="nf">since</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nf">CommandArguments</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;t&#34;</span><span class="p">,</span> <span class="s">&#34;top&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">c</span><span class="p">.</span><span class="nf">top</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s">&#34;test&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">c</span><span class="p">.</span><span class="nf">test</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">c</span><span class="p">.</span><span class="nf">sendText</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;Eh? /%s?&#34;</span><span class="p">,</span> <span class="nx">command</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">c</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="test">/test</h2>
<p>The simplest command would be <code>/test</code>, it just sends some hardcoded text back. It&rsquo;s pretty trivial to implement:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">context</span><span class="p">)</span> <span class="nf">test</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">c</span><span class="p">.</span><span class="nx">bot</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">tgbotapi</span><span class="p">.</span><span class="nf">NewMessage</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">Chat</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span> <span class="s">&#34;It works&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><img src="https://i.imgur.com/mX4RFc8.png" alt="test"></p>
<h2 id="export">/export</h2>
<p>Export is a very important command. I consider it so important that I implemented it first. It allows the user to download all of its data stored in the bot&rsquo;s database. Say no to vendor lock-in.</p>
<p>This is really annoying these days. Take any app, like a fitness tracker app for example. It keeps track of your runs and stores your data somewhere on the phone or in the cloud. And one day it stops working or you decide to start using another app. And what do you do? How do you migrate your data? Usually, there&rsquo;s no easy way or no way at all. You&rsquo;d have to abandon all of your history and your progress and start over in the new app.</p>
<p>I don&rsquo;t want that for my yet non-existent users. Let them take their data home whenever they want. That&rsquo;s why I have the <code>/export</code> command. Just say <code>/export</code> to the bot and it&rsquo;s happy to comply:</p>
<p><img src="https://i.imgur.com/pjPYAe1.png" alt="export"></p>
<p>To generate the CSV file I use the built-in package <code>encoding/csv</code>. It&rsquo;s very easy to export all the rows into a CSV file :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="nx">buffer</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">csv</span> <span class="o">:=</span> <span class="nx">csv</span><span class="p">.</span><span class="nf">NewWriter</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// For each row
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="o">...</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">csv</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="nx">name</span><span class="p">,</span> <span class="nx">date</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Flush when done (important!)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">csv</span><span class="p">.</span><span class="nf">Flush</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Send
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">c</span><span class="p">.</span><span class="nx">bot</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">tgbotapi</span><span class="p">.</span><span class="nf">NewDocumentUpload</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">c</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">Chat</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tgbotapi</span><span class="p">.</span><span class="nx">FileBytes</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Name</span><span class="p">:</span>  <span class="s">&#34;data.csv&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Bytes</span><span class="p">:</span> <span class="nx">buffer</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">()}))</span>
</span></span></code></pre></div><h2 id="add-and-since">/add and /since</h2>
<p>The <code>/add</code> and the <code>/since</code> commands are bread and butter of this bot. As the names imply, one is for adding the events and the other is for checking when the event was last added. <code>/add</code> is a 2-in-1 command, like shampoo &amp; conditioner in one bottle, it displays the time before adding.</p>
<p><img src="https://i.imgur.com/H2rHcY9.png" alt="since-add"></p>
<h2 id="top">/top</h2>
<p>To practice SQL, I added a relatively useless command to display 10 most logged events.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="n">freq</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">events</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">WHERE</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&lt;</span><span class="k">user</span><span class="o">-</span><span class="n">id</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">freq</span><span class="w"> </span><span class="k">DESC</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">10</span><span class="w">
</span></span></span></code></pre></div><p>On my really dumb dataset it looks like this:</p>
<p><img src="https://i.imgur.com/sRNYKrk.png" alt="top"></p>
<p>I have to say that I&rsquo;m pretty impressed with SQL so far. I knew theoretically what it could do. But now I get to try it and it&rsquo;s really cool how I can just write a simple query instead of writing a bunch of code with loops and variables. Probably more efficient too.</p>
<h2 id="whats-next">What&rsquo;s next</h2>
<p>The bot is getting smarter. Now it could respond in different ways to a few basic commands. More is coming. I&rsquo;d like the bot to be able to draw charts and show some statistics, like how often something happened or distribution throughout the day/week/month. Do <em>you</em> have any ideas?</p>
<p><img src="https://i.imgur.com/jR47I5p.png" alt="ideas"></p>
<p>If you&rsquo;re curious, the code is <a href="https://github.com/detunized/since-bot/tree/day-5">available on GitHub</a>. This version is tagged <code>day-5</code>.</p>
<p><em>Also published on <a href="https://dev.to/detunized/telegram-bot-in-go-speak-robot-1l9o">DEV</a> and <a href="https://medium.com/@detunized/telegram-bot-in-go-speak-robot-16c54d655455">Medium</a></em></p>
]]></content></item><item><title>Telegram bot in Go: speak human</title><link>https://detunized.net/posts/2019-04-04-telegram-bot-in-go-speak-human/</link><pubDate>Thu, 04 Apr 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-04-04-telegram-bot-in-go-speak-human/</guid><description>Last time I was bulletproofing my SQLite access foundation. Let&amp;rsquo;s see if it holds in production. Too bad I&amp;rsquo;m never gonna get that high load that is supposed to break things. Oh well, let&amp;rsquo;s wait and see. Maybe I get lucky and two sad concurrent requests would eventually make my Go binary panic.
Today, I&amp;rsquo;m going to focus on features. I&amp;rsquo;d like to make my bot intelligent. Not really AI like, rather just sounding a bit better than cmd.</description><content type="html"><![CDATA[<p><a href="https://detunized.net/posts/2019-04-01-telegram-bot-in-go-concurrent-sqlite/">Last time</a> I was bulletproofing my SQLite access foundation. Let&rsquo;s see if it holds in production. Too bad I&rsquo;m never gonna get that high load that is supposed to break things. Oh well, let&rsquo;s wait and see. Maybe I get lucky and two sad concurrent requests would eventually make my Go binary panic.</p>
<p>Today, I&rsquo;m going to focus on features. I&rsquo;d like to make my bot <em>intelligent</em>. Not really AI like, rather just sounding a bit better than <code>cmd.exe</code> when you type in a wrong command. I&rsquo;m not gonna make anything big, more like dipping the toe in the water to see how it is.</p>
<p>First, I&rsquo;d like to find the last event with the same name, since I&rsquo;m storing them in the database, and send back the date the event. Imagine the interaction like this:</p>
<pre tabindex="0"><code>me: eat
bot: Previous &#39;eat&#39; happened 6 hours ago
</code></pre><p>To find the event with the same name I have to ask SQLite to give me the row where the user is the same, the name of the event it the same and I want the last row after it&rsquo;s sorted by date. Converting English to SQL I get:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="nb">date</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">events</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">WHERE</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">?</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="nb">date</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">DESC</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">1</span><span class="w">
</span></span></span></code></pre></div><p>Pretty simple, eh? Now, converting this to Go and <code>crawshaw.io/sqlite</code> I get the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="c1">// Default response
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">response</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;Fist time for &#39;%s&#39;&#34;</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Get the last event with the same name and format the response
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlitex</span><span class="p">.</span><span class="nf">Exec</span><span class="p">(</span><span class="nx">connection</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;SELECT date FROM events &#34;</span><span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;WHERE user = ? AND name = ? &#34;</span><span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ORDER BY date &#34;</span><span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;DESC LIMIT 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="kd">func</span><span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">sqlite</span><span class="p">.</span><span class="nx">Stmt</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">response</span> <span class="p">=</span> <span class="nf">formatResponse</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nf">GetInt64</span><span class="p">(</span><span class="s">&#34;date&#34;</span><span class="p">),</span> <span class="nx">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">message</span><span class="p">.</span><span class="nx">From</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Send the message back to the user
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">bot</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">tgbotapi</span><span class="p">.</span><span class="nf">NewMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Chat</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span> <span class="nx">response</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}()</span>
</span></span></code></pre></div><p>Where <code>formatResponse</code> is very rudimentary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">formatResponse</span><span class="p">(</span><span class="nx">date</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">name</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">last</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Unix</span><span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;Previous &#39;%s&#39; happened on &#39;%v&#39;&#34;</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">last</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now the interaction looks like this:</p>
<p><img src="https://i.imgur.com/mqD0QVf.png" alt="since-bot-1"></p>
<p>Not exactly the smartest bot on the planet, but we&rsquo;re going somewhere.</p>
<p>Now, I would like to make it sound a bit less dumb and bit more human, which are not always going together. In this case, they are. I don&rsquo;t want the bot to say things like <code>happened on '2019-04-04 14:13:57 +0200 CEST'</code>, but rather something like <code>8 minutes since</code> or <code>1 year since</code>. Believe it or not, but there&rsquo;s a package for exactly that. Welcome <a href="https://github.com/hako/durafmt">hako/durafmt</a>. With its help it&rsquo;s very easy to turn the conversation into something like this:</p>
<p><img src="https://i.imgur.com/1SBahPw.png" alt="since-bot-2"></p>
<p>It is much more readable. To make it work I just had to modify the <code>formatResponse</code> function a bit (now it also has to take the current message date):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">formatResponse</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">date</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">prevDate</span> <span class="kt">int64</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">prev</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Unix</span><span class="p">(</span><span class="nx">prevDate</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">now</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Unix</span><span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">duration</span> <span class="o">:=</span> <span class="nx">durafmt</span><span class="p">.</span><span class="nf">ParseShort</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Sub</span><span class="p">(</span><span class="nx">prev</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s since last &#39;%s&#39;&#34;</span><span class="p">,</span> <span class="nx">duration</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Easy peasy. With not so many lines and in under an hour of work I have a bot that speaks human and can tell me when something happened the last time. Ship it! 🚢</p>
<p>If you&rsquo;re curious, the code is <a href="https://github.com/detunized/since-bot/tree/day-4">available on GitHub</a>. This version is tagged <code>day-4</code>.</p>
<p><em>Also published on <a href="https://dev.to/detunized/telegram-bot-in-go-speak-human-5bkd">DEV</a> and <a href="https://medium.com/@detunized/telegram-bot-in-go-speak-human-b2b4547cad29">Medium</a></em></p>
]]></content></item><item><title>Telegram bot in Go: concurrent SQLite</title><link>https://detunized.net/posts/2019-04-01-telegram-bot-in-go-concurrent-sqlite/</link><pubDate>Mon, 01 Apr 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-04-01-telegram-bot-in-go-concurrent-sqlite/</guid><description>Last time I added SQLite to my bot and at the same time I moved the request processing into a goroutine. Which means I introduced concurrent database access to my codebase.
Normally one should think, then do. Though not ideal, it&amp;rsquo;s also possible to do it the other way around when you use git. But it&amp;rsquo;s important not to forget to think at some point. Like I almost did and almost moved on to pile on more bugs on top of what I just introduced.</description><content type="html"><![CDATA[<p><a href="https://detunized.net/posts/2019-03-29-telegram-bot-in-go-database/">Last time</a> I added SQLite to my bot and at the same time I moved the request processing into a goroutine. Which means I introduced concurrent database access to my codebase.</p>
<p>Normally one should think, then do. Though not ideal, it&rsquo;s also possible to do it the other way around when you use <code>git</code>. But it&rsquo;s important not to forget to think at some point. Like I almost did and almost moved on to pile on more bugs on top of what I just introduced.</p>
<p>I remembered reading or watching something about SQLite the other day and they made it clear that I&rsquo;d have to make sure not to access the database from multiple places at the same time. Go is made for concurrency, so I&rsquo;m sure <a href="https://github.com/mattn/go-sqlite3">go-sqlite3</a> I was using has it under control. Not exactly, as it turned out.</p>
<p>From the <a href="https://github.com/mattn/go-sqlite3#faq">FAQ</a>:</p>
<blockquote>
<p>Can I use this in multiple routines concurrently?</p>
<p>Yes for readonly. But, No for writable. See <a href="https://github.com/mattn/go-sqlite3/issues/50">#50</a>, <a href="https://github.com/mattn/go-sqlite3/issues/51">#51</a>, <a href="https://github.com/mattn/go-sqlite3/issues/209">#209</a>, <a href="https://github.com/mattn/go-sqlite3/issues/274">#274</a>.</p>
</blockquote>
<p>And just like that I went down the major rabbit hole. I searched and researched, googled and binged (no, not really), read the source of <code>go-sqlite3</code> and the docs for SQLite (which are pretty good by the way). All of that just to find myself in a situation that I don&rsquo;t know if I can trust the <code>go-sqlite3</code> package to handle my database work. Even though the FAQ states that write concurrency is not supported, I found contradicting statements. People were saying it&rsquo;s fine if I use multiple connections. But I wasn&rsquo;t going to open a new connection every time.</p>
<p>Long story short, I found a <a href="https://crawshaw.io/blog/go-and-sqlite">blog post</a> which addressed exactly the same problem and offered a solution for it (and a couple of others) in a form of a <a href="https://github.com/crawshaw/sqlite">Go package</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">go get -u crawshaw.io/sqlite
</span></span></code></pre></div><p>According to David, the author of the post and the package, I should be able to trust this package to handle concurrency reliably. Now, I don&rsquo;t simply establish a connection to the database, but rather create an explicit pool of those of the size I desire (16 in this case):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;crawshaw.io/sqlite/sqlitex&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">openDB</span><span class="p">()</span> <span class="o">*</span><span class="nx">sqlitex</span><span class="p">.</span><span class="nx">Pool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">db</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlitex</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="s">&#34;./since.db&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">db</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now, every time I want to access the database I have to get a connection from the pool and not to forget to put it back when I&rsquo;m done. Getting a connection from the pool might block until a connection becomes available.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">execSQL</span><span class="p">(</span><span class="nx">db</span> <span class="o">*</span><span class="nx">sqlitex</span><span class="p">.</span><span class="nx">Pool</span><span class="p">,</span> <span class="nx">sql</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">connection</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Put</span><span class="p">(</span><span class="nx">connection</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlitex</span><span class="p">.</span><span class="nf">Exec</span><span class="p">(</span><span class="nx">connection</span><span class="p">,</span> <span class="nx">sql</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>A side note on the error handling. I currently do not handle errors on purpose to not slow myself down. But I don&rsquo;t ignore them either. Like any self-respecting software engineer I panic when I receive an error. For some errors, like not being able to open the database on startup, it&rsquo;s totally fine to panic. But if there was an error with one of the messages, panic is not such a good choice. It&rsquo;s like shutting down the server when we should have just returned HTTP/404.</p>
<p>Now, storing the incoming message in the database becomes this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">store</span><span class="p">(</span><span class="nx">message</span> <span class="o">*</span><span class="nx">tgbotapi</span><span class="p">.</span><span class="nx">Message</span><span class="p">,</span> <span class="nx">db</span> <span class="o">*</span><span class="nx">sqlitex</span><span class="p">.</span><span class="nx">Pool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">connection</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Put</span><span class="p">(</span><span class="nx">connection</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlitex</span><span class="p">.</span><span class="nf">Exec</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nx">connection</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;INSERT INTO events (user, name, date) VALUES (?, ?, ?);&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="p">.</span><span class="nx">From</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="p">.</span><span class="nx">Text</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="p">.</span><span class="nx">Date</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>One small thing I don&rsquo;t like about this is that I&rsquo;m forced to use positional SQL arguments if I want to use <code>sqlitex.Exec</code>. If I wanted to use the column names like <code>&quot;... VALUES ($user, $name, $date)&quot;</code>, I&rsquo;d have to use a much wordier API. Prepare statement myself and then step through it. Like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">store</span><span class="p">(</span><span class="nx">message</span> <span class="o">*</span><span class="nx">tgbotapi</span><span class="p">.</span><span class="nx">Message</span><span class="p">,</span> <span class="nx">db</span> <span class="o">*</span><span class="nx">sqlitex</span><span class="p">.</span><span class="nx">Pool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">connection</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Put</span><span class="p">(</span><span class="nx">connection</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">insert</span> <span class="o">:=</span> <span class="nx">connection</span><span class="p">.</span><span class="nf">Prep</span><span class="p">(</span><span class="s">&#34;INSERT INTO events (user, name, date) VALUES ($user, $name, $date);&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">insert</span><span class="p">.</span><span class="nf">SetInt64</span><span class="p">(</span><span class="s">&#34;$user&#34;</span><span class="p">,</span> <span class="nb">int64</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">From</span><span class="p">.</span><span class="nx">ID</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nx">insert</span><span class="p">.</span><span class="nf">SetText</span><span class="p">(</span><span class="s">&#34;$name&#34;</span><span class="p">,</span> <span class="nx">message</span><span class="p">.</span><span class="nx">Text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">insert</span><span class="p">.</span><span class="nf">SetInt64</span><span class="p">(</span><span class="s">&#34;$date&#34;</span><span class="p">,</span> <span class="nb">int64</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Date</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">insert</span><span class="p">.</span><span class="nf">Step</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Done with this query
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// TODO: Is it really needed? What happens when this isn&#39;t called?
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">err</span> <span class="p">=</span> <span class="nx">insert</span><span class="p">.</span><span class="nf">Reset</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If I see that positional arguments become a problem, I&rsquo;ll switch to this way of doing things.</p>
<p>As much as I wanted to make my bot less dumb this time, I only managed to switch libraries while trying to bullet-proof my SQLite access methods. Just refactoring, no features. Here&rsquo;s a typical day of a software engineer for you. Well, the next day then.</p>
<p>If you&rsquo;re curious, the code is <a href="https://github.com/detunized/since-bot/tree/day-3">available on GitHub</a>. This version is tagged <code>day-3</code>.</p>
<p><em>Also published on <a href="https://dev.to/detunized/telegram-bot-in-go-concurrent-sqlite-343i">DEV</a> and <a href="https://medium.com/@detunized/telegram-bot-in-go-concurrent-sqlite-e6176fac088e">Medium</a></em></p>
]]></content></item><item><title>Telegram bot in Go: database</title><link>https://detunized.net/posts/2019-03-29-telegram-bot-in-go-database/</link><pubDate>Fri, 29 Mar 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-03-29-telegram-bot-in-go-database/</guid><description>Today I&amp;rsquo;m gonna add a database to my bot. As I mentioned in the previous post, I&amp;rsquo;m going to use SQLite to keep it simple and because the management turned down my budget request for Oracle on this project.
I quickly shopped around for an SQLite package for Go and found go-sqlite3. This seems to be a very popular package. It allows me to use the standard database/sql package and it acts as a driver, which allows the Go database engine to talk to SQLite databases.</description><content type="html"><![CDATA[<p>Today I&rsquo;m gonna add a database to my bot. As I mentioned in the <a href="https://detunized.net/posts/2019-03-28-telegram-bot-in-go/">previous post</a>, I&rsquo;m going to use SQLite to keep it simple and because the management turned down my budget request for Oracle on this project.</p>
<p>I quickly shopped around for an SQLite package for Go and found <a href="https://github.com/mattn/go-sqlite3">go-sqlite3</a>. This seems to be a very popular package. It allows me to use the standard <code>database/sql</code> package and it acts as a driver, which allows the Go database engine to talk to SQLite databases. So far so good. I&rsquo;ll use that then.</p>
<p>Since I&rsquo;m writing everything in Go, I desperately need to use goroutines somewhere. So the first thing I did was to move the reply functionality into a goroutine.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">update</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">updates</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">go</span> <span class="nf">reply</span><span class="p">(</span><span class="nx">bot</span><span class="p">,</span> <span class="nx">update</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That should make things go <strong>FAST</strong>! Never mind the fact that Telegram will <a href="https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this">not allow</a> frequent requests for any extended period of time. I&rsquo;ve gotta try anyway.</p>
<p>To use the database I have to open it first. It&rsquo;s done like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">openDatabase</span><span class="p">()</span> <span class="o">*</span><span class="nx">sql</span><span class="p">.</span><span class="nx">DB</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">db</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sql</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="s">&#34;sqlite3&#34;</span><span class="p">,</span> <span class="s">&#34;./since.db&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Exec</span><span class="p">(</span><span class="s">&#34;CREATE TABLE IF NOT EXISTS events (&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;user INTEGER, &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;name TEXT, &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;date INTEGER);&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">db</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I also create a table if it doesn&rsquo;t exist. Since I barely use SQL, I prefer to SCREAM so people can hear me. And maybe the DB will get a <a href="https://stackoverflow.com/a/35684720/362938">sense of urgency</a> and <a href="https://twitter.com/shipilev/status/703176579191410689">process my queries faster</a>.</p>
<p>Once the database successfully opened and no one panicked, it&rsquo;s time to start saving incoming events to it. I do it like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">store</span><span class="p">(</span><span class="nx">message</span> <span class="o">*</span><span class="nx">tgbotapi</span><span class="p">.</span><span class="nx">Message</span><span class="p">,</span> <span class="nx">db</span> <span class="o">*</span><span class="nx">sql</span><span class="p">.</span><span class="nx">DB</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nf">Exec</span><span class="p">(</span><span class="s">&#34;INSERT INTO events (user, name, date) VALUES ($1, $2, $3);&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="p">.</span><span class="nx">From</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="p">.</span><span class="nx">Text</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="p">.</span><span class="nx">Date</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">store</span><span class="p">(</span><span class="nx">update</span><span class="p">.</span><span class="nx">Message</span><span class="p">,</span> <span class="nx">db</span><span class="p">)</span>
</span></span></code></pre></div><p>Now, every time I send a message to my bot it stores its content in the database. It&rsquo;s a start. The bot is still responding with the same reply as before. It&rsquo;s still really dumb. We&rsquo;ll get to make it smarter in the next day.</p>
<p>If you&rsquo;re curious, the code is <a href="https://github.com/detunized/since-bot/tree/day-2">available on GitHub</a>. This version is tagged <code>day-2</code>.</p>
<p><em>Also published on <a href="https://dev.to/detunized/telegram-bot-in-go-database-25mn">DEV</a> and <a href="https://medium.com/@detunized/telegram-bot-in-go-database-f8714381c858">Medium</a></em></p>
]]></content></item><item><title>Telegram bot in Go</title><link>https://detunized.net/posts/2019-03-28-telegram-bot-in-go/</link><pubDate>Thu, 28 Mar 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-03-28-telegram-bot-in-go/</guid><description>This is my fourth or fifth attempt to like Go. Kinda didn&amp;rsquo;t work all the previous times. I blame myself, though. I believe I chose Go for the wrong types of projects. This time it&amp;rsquo;s what&amp;rsquo;s it was made to do. It&amp;rsquo;s a backend application, it&amp;rsquo;s deployed to a remote server, it&amp;rsquo;s using concurrency and not too complex. It&amp;rsquo;s a Telegram bot that helps me track random things.
Idea I want to be able to send simple commands to the chat and let the bot remember what happened and when.</description><content type="html"><![CDATA[<p>This is my fourth or fifth attempt to like Go. Kinda didn&rsquo;t work all the previous times. I blame myself, though. I believe I chose Go for the wrong types of projects. This time it&rsquo;s what&rsquo;s it was made to do. It&rsquo;s a backend application, it&rsquo;s deployed to a remote server, it&rsquo;s using concurrency and not too complex. It&rsquo;s a Telegram bot that helps me track random things.</p>
<h2 id="idea">Idea</h2>
<p>I want to be able to send simple commands to the chat and let the bot remember what happened and when. It&rsquo;s a bit like a log file. I want to be able to ask the bot when or how often something happened. This is what it could look like:</p>
<pre tabindex="0"><code>me: eat
bot: Recorded &#39;eat&#39;. Last &#39;eat&#39; happened 8 hours ago.

me: sleep, 10 hours ago
bot: Recorded &#39;sleep&#39; at 23:15, yesterday.
     Last &#39;sleep&#39; happened 1 day 12 hours ago.

me: /since run
bot: Last &#39;run&#39; happened 2 days 15 hours ago.

me: /stats blog
bot: So far &#39;blog&#39; happened 20 times, average
     time between events is 4 days 5 hours.
</code></pre><p>The questions and answers are a bit clumsy, but I don&rsquo;t think I&rsquo;m going to implement the whole natural language processing thing here. It&rsquo;s a simple bot, remember? I have ideas for many more commands that could be useful, but I&rsquo;ll start small with recording and basic querying.</p>
<h2 id="architecture">Architecture</h2>
<p>It&rsquo;s kind of a big word, eh? To talk to the Telegram servers I&rsquo;m going to use <a href="https://github.com/go-telegram-bot-api/telegram-bot-api">Go bindings for the Telegram Bot API</a>. I&rsquo;m planning on using webhooks in the final version. I&rsquo;ll start with the poll loop though since it&rsquo;s possible to run the polling bot locally.</p>
<p>I&rsquo;m planning to use SQLite to store the data. I like zero configuration and setup. I like that I could just copy the database anywhere I want. This way I could organize backups very easily. For example, I could just email it to myself every now and then. Unless I open the bot to the world, I don&rsquo;t expect the database to grow much.</p>
<h2 id="code">Code</h2>
<p>Let&rsquo;s jump straight to coding then. The Go Telegram Bot API provides a good <a href="https://github.com/go-telegram-bot-api/telegram-bot-api/blob/master/README.md#example">starting point</a> in the README. It&rsquo;s an echo bot. It just sends the received text back to the user. I&rsquo;ll start with that.</p>
<p>For the bot to connect to the Telegram network it has to have a username and an API token. Both thing you could get by talking to a special bot on Telegram. The bot is called <a href="https://t.me/BotFather">The Botfather</a>.</p>
<p><img src="https://i.imgur.com/zQRcF0w.png" alt="The Botfather"></p>
<p>The interaction is quite trivial and pleasant. Just follow the dialog and he&rsquo;ll give you what you need. Just make sure not to ask for too much.</p>
<p><img src="https://i.imgur.com/eCAQ4q5.jpg" alt="20 bots"></p>
<p>The API token I got from the Botfather should be kept private. Who owns the token owns the bot. To keep it out of the repo I added a bit of code to read it from a simple JSON config. Besides that, it&rsquo;s just the code from the README.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Config represents the structure of the config.json file
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Config</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Token</span> <span class="kt">string</span> <span class="s">`json:&#34;token&#34;`</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">readConfig</span><span class="p">()</span> <span class="nx">Config</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">file</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="s">&#34;config.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">defer</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">bytes</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">config</span> <span class="nx">Config</span>
</span></span><span class="line"><span class="cl">    <span class="nx">err</span> <span class="p">=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">bytes</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">config</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That should do it for day 1. If you&rsquo;re curious, the code is <a href="https://github.com/detunized/since-bot/tree/day-1">available on GitHub</a>. This version is tagged <code>day-1</code>.</p>
<p><em>Also published on <a href="https://dev.to/detunized/telegram-bot-in-go-3hd4">DEV</a> and <a href="https://medium.com/@detunized/telegram-bot-in-go-9956786f60d0">Medium</a></em></p>
]]></content></item><item><title>NUnit to xUnit automatic test conversion: source code transformation</title><link>https://detunized.net/posts/2019-03-26-nunit-to-xunit-automatic-test-conversion-source-code-transformation/</link><pubDate>Tue, 26 Mar 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-03-26-nunit-to-xunit-automatic-test-conversion-source-code-transformation/</guid><description>In the previous post I wrote about how I find the patterns in the code that I would like to refactor using simple C# syntax. Basically, I write the exact expression I would like to find with some wildcards that match the varying parts and the rest is matched as is. A bit like a regexp or a shell file glob. Like this:
Assert.That(_, Is.EqualTo(_)) Assert.That(_, Throws.TypeOf&amp;lt;_&amp;gt;()) or
_._(_, _) if you&amp;rsquo;d like to go extreme and match every member function call with two parameters.</description><content type="html"><![CDATA[<p>In the <a href="https://detunized.net/posts/2019-03-16-nunit-to-xunit-automatic-test-conversion-pattern-match/">previous post</a> I wrote about how I find the patterns in the code that I would like to refactor using simple C# syntax. Basically, I write the exact expression I would like to find with some wildcards that match the varying parts and the rest is matched as is. A bit like a regexp or a shell file glob. Like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">_</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">Throws</span><span class="p">.</span><span class="n">TypeOf</span><span class="p">&lt;</span><span class="n">_</span><span class="p">&gt;())</span>
</span></span></code></pre></div><p>or</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">_</span><span class="p">.</span><span class="n">_</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span>
</span></span></code></pre></div><p>if you&rsquo;d like to go extreme and match every member function call with two parameters.</p>
<p>What I would like to be able to do, though, is to transform the code, not just match. I&rsquo;d like to specify how to convert the patterns to the form I&rsquo;m after. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">@actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">@expected</span><span class="p">))</span> <span class="p">-&gt;</span> <span class="n">Assert</span><span class="p">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">@expected</span><span class="p">,</span> <span class="n">@actual</span><span class="p">)</span>
</span></span></code></pre></div><p>Well, this is exactly the syntax I&rsquo;m going to use. The placeholders <code>@actual</code> and <code>@expected</code> match any expression subtrees in the AST, anything that could be placed as arguments in those two positions. It&rsquo;s actually a valid C# code as it allows <code>@</code> at the beginning of an identifier. In my matcher I treat any identifier that starts with an <code>@</code> as a placeholder.</p>
<p>The code in the previous post only matched the patterns (<code>_</code> matches anything, a bit like <code>.*</code> in a regex). Now we need to store the named matches, like the match groups in a regex. The code would have to be changed from</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// A placeholder matches anything</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">IsPlaceholder</span><span class="p">(</span><span class="n">pattern</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span></code></pre></div><p>to</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// A placeholder matches anything</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">IsPlaceholder</span><span class="p">(</span><span class="n">pattern</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="n">pattern</span><span class="p">.</span><span class="n">ToFullString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">name</span> <span class="p">!=</span> <span class="s">&#34;_&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">Matches</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="p">=</span> <span class="n">code</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The rest of the matching code stays practically untouched. We have a matcher, the next step would be to write a <em>&ldquo;substituter&rdquo;</em> or <em>&ldquo;replacer&rdquo;</em>, the <em>replace</em> part of the find-and-replace tool.</p>
<p>Since I like to keep my code <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a>, first I spent a bunch of time thinking about how to reuse the matching code for the substitution part. I failed there. So I decided to go with <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself#DRY_vs_WET_solutions">WET</a> instead and rewrite that giant switch once again.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// _variables is populated in the constructor</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">SyntaxNode</span> <span class="n">Replace</span><span class="p">(</span><span class="n">SyntaxNode</span> <span class="n">template</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// A placeholder found. Substitute.</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">IsPlaceholder</span><span class="p">(</span><span class="n">template</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">name</span> <span class="p">=</span> <span class="n">template</span><span class="p">.</span><span class="n">ToFullString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">_variables</span><span class="p">.</span><span class="n">TryGetValue</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">v</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">v</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">template</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">template</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">ArgumentSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">NameColon</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">t</span><span class="p">.</span><span class="n">RefKindKeyword</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">(</span><span class="n">ExpressionSyntax</span><span class="p">)</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Expression</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">ArgumentListSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">OpenParenToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Arguments</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">t</span><span class="p">.</span><span class="n">CloseParenToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">IdentifierNameSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">(</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Identifier</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">InvocationExpressionSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">((</span><span class="n">ExpressionSyntax</span><span class="p">)</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Expression</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="p">(</span><span class="n">ArgumentListSyntax</span><span class="p">)</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">ArgumentList</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">LiteralExpressionSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">(</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Token</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">MemberAccessExpressionSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">((</span><span class="n">ExpressionSyntax</span><span class="p">)</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Expression</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="n">t</span><span class="p">.</span><span class="n">OperatorToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="p">(</span><span class="n">SimpleNameSyntax</span><span class="p">)</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Name</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">GenericNameSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">(</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Identifier</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="p">(</span><span class="n">TypeArgumentListSyntax</span><span class="p">)</span><span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">TypeArgumentList</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">TypeArgumentListSyntax</span> <span class="n">t</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">t</span><span class="p">.</span><span class="n">Update</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">LessThanToken</span><span class="p">,</span> <span class="n">Replace</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Arguments</span><span class="p">),</span> <span class="n">t</span><span class="p">.</span><span class="n">GreaterThanToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">template</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This code recursively walks the replace template AST and substitutes the placeholders (like <code>@actual</code> and <code>@expected</code>) with the matches found in the previous step. The end result of this substitution is then swapped with the matched expression node in the original source file AST. And that&rsquo;s it. Works like a charm.</p>
<blockquote>
<p>A side note on the choice of keywords here. I&rsquo;m not an OOP aficionado. I think it has its place and in C# it&rsquo;s quite natural to use classes and member functions to represent and do stuff. Though most of the time I try to stick to more of a functional style, where functions don&rsquo;t have any hidden state and take all their input as parameters and return the result.  Most of my functions and classes are static, actually. But common sense wins most of the time for me (or so I hope). So, in this case, I went with a class and non-static functions to not to pass around the state into every function, as there are quite many recursive calls. I kept it DRY in a sense.</p>
</blockquote>
<p>I wrote a small driver application for this algorithm. It takes care of the loading, parsing, writing out the result. All the patterns are hardcoded for now, but there&rsquo;s nothing but my laziness stopping me from putting that into a config file.</p>
<p>Here are the patterns I used to convert the bulk of my tests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">@actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span>      <span class="p">-&gt;</span> <span class="n">Assert</span><span class="p">.</span><span class="n">True</span><span class="p">(</span><span class="n">@actual</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">@actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span>     <span class="p">-&gt;</span> <span class="n">Assert</span><span class="p">.</span><span class="n">False</span><span class="p">(</span><span class="n">@actual</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">@actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">@expected</span><span class="p">))</span> <span class="p">-&gt;</span> <span class="n">Assert</span><span class="p">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">@expected</span><span class="p">,</span> <span class="n">@actual</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">@code</span><span class="p">,</span> <span class="n">Throws</span><span class="p">.</span><span class="n">TypeOf</span><span class="p">&lt;</span><span class="n">@type</span><span class="p">&gt;())</span>  <span class="p">-&gt;</span> <span class="n">Assert</span><span class="p">.</span><span class="n">Throws</span><span class="p">&lt;</span><span class="n">@type</span><span class="p">&gt;(</span><span class="n">@code</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>The solution described here is not a working tool ready to be picked up and used by anyone. It&rsquo;s a proof of concept that happens to work and it has value. At least for me. I was able to convert hundreds of tests across dozens of files, saving myself a couple of hours of tedious manual labor. The time I put into this tool I didn&rsquo;t get back. Pure ROI is negative here. But it&rsquo;s not only about time. I got plenty of satisfaction from learning something new, from automating an annoying task, from blogging about it and sharing it with the world.</p>
<p>The code could be found <a href="https://github.com/detunized/nunit2xunit">here</a>.</p>
<h2 id="future-work">Future work</h2>
<p>Not every AST node type is covered by the pattern matching code and not every possible <code>Assert</code> expression is covered by the patterns. That is something to expand on. Maybe this could also be turned into a Visual Studio [Code] extension. In general this doesn&rsquo;t have to be a unit test conversion tool. With this mini-DSL it&rsquo;s possible to refactor/convert any code really. It&rsquo;s also possible to search for the matches in the codebase. Lots of ideas and not so much time.</p>
<p><em>Also published on <a href="https://dev.to/detunized/nunit-to-xunit-automatic-test-conversion-source-code-transformation-16pc">DEV</a> and <a href="https://medium.com/@detunized/82e8529fd415">Medium</a></em></p>
]]></content></item><item><title>NUnit to xUnit automatic test conversion: pattern match</title><link>https://detunized.net/posts/2019-03-16-nunit-to-xunit-automatic-test-conversion-pattern-match/</link><pubDate>Sat, 16 Mar 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-03-16-nunit-to-xunit-automatic-test-conversion-pattern-match/</guid><description>In the previous post I described how to use the Roslyn API to find code patterns in the C# AST and how to change the AST to rewrite the original code to something else. The goal was to automate the conversion of NUnit tests to xUnit. The approach I used was quite tedious, as I had to write a very long chain or ifs and typecasts to get the job done.</description><content type="html"><![CDATA[<p>In the <a href="https://detunized.net/posts/2019-03-12-nunit-to-xunit-automatic-test-conversion/">previous post</a> I described how to use the Roslyn API to find code patterns in the C# AST and how to change the AST to rewrite the original code to something else. The goal was to automate the conversion of NUnit tests to xUnit. The approach I used was quite tedious, as I had to write a very long chain or ifs and typecasts to get the job done. Let&rsquo;s try to do better this time. Let&rsquo;s start with just the search part in our search-and-replace tool.</p>
<p>What would be great is to be able to specify structural patterns like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">_</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">Throws</span><span class="p">.</span><span class="n">TypeOf</span><span class="p">&lt;</span><span class="n">_</span><span class="p">&gt;())</span>
</span></span></code></pre></div><p>And they would match the actual code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// Matched by &#39;Assert.That(_, Is.EqualTo(_))&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">account</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">id</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">.</span><span class="n">ToBytes</span><span class="p">(),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="k">new</span> <span class="kt">byte</span><span class="p">[]</span> <span class="p">{}))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Matched by &#39;Assert.That(_, Is.EqualTo(true))&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">info</span><span class="p">.</span><span class="n">IsMd5</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="kc">true</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">token</span><span class="p">.</span><span class="n">BoolAt</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="kc">true</span><span class="p">),</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="kc">true</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Matched by &#39;Assert.That(_, Is.Throws.TypeOf&lt;_&gt;())&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">Quad</span><span class="p">[-</span><span class="m">1</span><span class="p">],</span> <span class="n">Throws</span><span class="p">.</span><span class="n">TypeOf</span><span class="p">&lt;</span><span class="n">ArgumentOutOfRangeException</span><span class="p">&gt;())</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">access</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="n">path</span><span class="p">),</span> <span class="n">Throws</span><span class="p">.</span><span class="n">TypeOf</span><span class="p">&lt;</span><span class="n">JTokenAccessException</span><span class="p">&gt;())</span>
</span></span></code></pre></div><p>At first it looks like a quite difficult task. But as it turns out in its simple form is not even that hard. I got the idea first when I was generating code for AST replacement with <a href="https://roslynquoter.azurewebsites.net/">Roslyn Quoter</a>. Looking at its source code I discovered a bunch of <code>Parse*</code> methods of the <a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.csharp.syntaxfactory?view=roslyn-dotnet"><code>SyntaxFactory</code> class</a>.</p>
<p>So basically one function call will parse the snippet and return an AST for the given pattern:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">patternAst</span> <span class="p">=</span> <span class="n">SyntaxFactory</span><span class="p">.</span><span class="n">ParseExpression</span><span class="p">(</span><span class="s">&#34;Assert.That(_, Is.EqualTo(_))&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>The one line above is equivalent to a wall of code like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">patternAst</span> <span class="p">=</span>
</span></span><span class="line"><span class="cl">    <span class="n">InvocationExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">MemberAccessExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">SyntaxKind</span><span class="p">.</span><span class="n">SimpleMemberAccessExpression</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Assert&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;That&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">WithArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">SeparatedList</span><span class="p">&lt;</span><span class="n">ArgumentSyntax</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">                <span class="k">new</span> <span class="n">SyntaxNodeOrToken</span><span class="p">[]{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Argument</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;_&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Token</span><span class="p">(</span><span class="n">SyntaxKind</span><span class="p">.</span><span class="n">CommaToken</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Argument</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="n">InvocationExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="n">MemberAccessExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                <span class="n">SyntaxKind</span><span class="p">.</span><span class="n">SimpleMemberAccessExpression</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Is&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                                <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;EqualTo&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">                        <span class="p">.</span><span class="n">WithArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="n">ArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                <span class="n">SingletonSeparatedList</span><span class="p">&lt;</span><span class="n">ArgumentSyntax</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">Argument</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                        <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;_&#34;</span><span class="p">))))))})));</span>
</span></span></code></pre></div><p>It feels like a total win already and we have not even done anything useful yet. But let&rsquo;s find this pattern in a source AST. First, we need to parse the file we&rsquo;re searching in:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">sourceAst</span> <span class="p">=</span> <span class="n">CSharpSyntaxTree</span><span class="p">.</span><span class="n">ParseText</span><span class="p">(</span><span class="n">File</span><span class="p">.</span><span class="n">ReadAllText</span><span class="p">(</span><span class="n">filename</span><span class="p">));</span>
</span></span></code></pre></div><p>This gives us the list of all expression nodes in the AST:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">nodes</span> <span class="p">=</span> <span class="n">sourceAst</span><span class="p">.</span><span class="n">GetRoot</span><span class="p">().</span><span class="n">DescendantNodes</span><span class="p">().</span><span class="n">OfType</span><span class="p">&lt;</span><span class="n">ExpressionSyntax</span><span class="p">&gt;();</span>
</span></span></code></pre></div><p>And now we find the nodes that match:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">e</span> <span class="k">in</span> <span class="n">nodes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">Ast</span><span class="p">.</span><span class="n">Match</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">patternAst</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">line</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">GetLocation</span><span class="p">().</span><span class="n">GetLineSpan</span><span class="p">().</span><span class="n">StartLinePosition</span><span class="p">.</span><span class="n">Line</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">code</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">NormalizeWhitespace</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;  {line}: {code}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Obviously the <code>Ast.Match</code> function is the tricky one. But not as tricky, really. We recursively traverse both ASTs in parallel and see if they match:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">bool</span> <span class="n">Match</span><span class="p">(</span><span class="n">SyntaxNode</span> <span class="n">code</span><span class="p">,</span> <span class="n">SyntaxNode</span> <span class="n">pattern</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// A placeholder matches anything</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">IsPlaceholder</span><span class="p">(</span><span class="n">pattern</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Node types don&#39;t match. Clearly not a match.</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">code</span><span class="p">.</span><span class="n">GetType</span><span class="p">()</span> <span class="p">!=</span> <span class="n">pattern</span><span class="p">.</span><span class="n">GetType</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">code</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">ArgumentSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">ArgumentSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Expression</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Expression</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">ArgumentListSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">ArgumentListSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">OpenParenToken</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">OpenParenToken</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">&amp;&amp;</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Arguments</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Arguments</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">&amp;&amp;</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">CloseParenToken</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">CloseParenToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">IdentifierNameSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">IdentifierNameSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Identifier</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Identifier</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">InvocationExpressionSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">InvocationExpressionSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Expression</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Expression</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">&amp;&amp;</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">ArgumentList</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">ArgumentList</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">LiteralExpressionSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">LiteralExpressionSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Token</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Token</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">MemberAccessExpressionSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">MemberAccessExpressionSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Expression</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Expression</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">&amp;&amp;</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">GenericNameSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">GenericNameSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Identifier</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Identifier</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">&amp;&amp;</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">TypeArgumentList</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">TypeArgumentList</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">TypeArgumentListSyntax</span> <span class="n">c</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="p">(</span><span class="n">TypeArgumentListSyntax</span><span class="p">)</span><span class="n">pattern</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">LessThanToken</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">LessThanToken</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">&amp;&amp;</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">Arguments</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">Arguments</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">&amp;&amp;</span> <span class="n">Match</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">GreaterThanToken</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">GreaterThanToken</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>So it&rsquo;s basically a giant switch with every node type in it. By far not every type is covered here, just those that I needed to get my examples to work. I imagine to cover the most of C# syntax I&rsquo;d have to tediously write a couple of thousand lines of repetitive code. I&rsquo;m not going to do it all any time soon. Just the stuff I need to cover my use cases.</p>
<p>With a <a href="https://gist.github.com/detunized/d02a0640986f44243dc01e5f50f42e74">few more lines of code added this already becomes a useful tool</a> for searching for code patterns in a codebase. Next time we see how we can implement the replace part. The goal was to refactor, not just to search, wasn&rsquo;t it? I have some ideas on how it could be done. See you next time.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Thanks to Roslyn awesome API with just <a href="https://gist.github.com/detunized/d02a0640986f44243dc01e5f50f42e74">172 lines of code</a> we have a pretty advanced code grep. Surely, it&rsquo;s just a toy and a proof of concept at the moment. It would take a serious effort to make it something more than that. But I&rsquo;m happy with what is possible with so little effort. Amazing.</p>
<p><em>Also published on <a href="https://dev.to/detunized/nunit-to-xunit-automatic-test-conversion-pattern-match-5alb">DEV</a> and <a href="https://medium.com/@detunized/nunit-to-xunit-automatic-test-conversion-pattern-match-9628360f74a5">Medium</a></em></p>
]]></content></item><item><title>NUnit to xUnit automatic test conversion</title><link>https://detunized.net/posts/2019-03-12-nunit-to-xunit-automatic-test-conversion/</link><pubDate>Tue, 12 Mar 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-03-12-nunit-to-xunit-automatic-test-conversion/</guid><description>I&amp;rsquo;m currently working on a major refactoring of a C# library which has many NUnit tests. I decided, without having any good reason, it would be a good idea to migrate them to xUnit. I did a few by hand and it turns out to be tedious. Like really tedious. The most common pattern is the following:
The test in NUnit
Assert.That(actual, Is.EqualTo(expected)); becomes the test in xUnit:
Assert.Equal(expected, actual); To convert each by hand requires a lot of patience and stamina.</description><content type="html"><![CDATA[<p>I&rsquo;m currently working on a major refactoring of a C# library which has many NUnit tests. I decided, without having any good reason, it would be a good idea to migrate them to xUnit. I did a few by hand and it turns out to be tedious. Like really tedious. The most common pattern is the following:</p>
<p>The test in NUnit</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="n">expected</span><span class="p">));</span>
</span></span></code></pre></div><p>becomes the test in xUnit:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">expected</span><span class="p">,</span> <span class="n">actual</span><span class="p">);</span>
</span></span></code></pre></div><p>To convert each by hand requires a lot of patience and stamina. Since I don&rsquo;t have either, after doing a few dozens manually I decided to automate the whole thing. The first most obvious approach would be to use a regexp and convert one to the other like so:</p>
<pre tabindex="0"><code>/Assert\.That\((.*?), Is\.EqualTo\((.*)\)\)/ -&gt; Assert.Equal($2, $1)
</code></pre><p>It does work for some simple cases, but throw in something a bit hairier and the whole thing goes sideways. A perfectly valid small test tears this regexp to shreds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="s">&#34;));&#34;</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">&#34;));&#34;</span><span class="p">));</span> <span class="p">-&gt;</span> <span class="n">Assert</span><span class="p">.</span><span class="n">Equal</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">));</span><span class="s">&#34;);;&#34;</span><span class="p">));</span>
</span></span></code></pre></div><p>Not good.</p>
<p>The better way to do that would be to parse the source file into an AST (Abstract Syntax Tree) and perform source to source transformations on it. I&rsquo;ve done a bit of this in the past with C++ using <a href="https://clang.llvm.org/">Clang/LLVM</a> and with JavaScript using <a href="https://github.com/acornjs/acorn">Acorn parser</a>. For C# there&rsquo;s <a href="https://github.com/dotnet/roslyn">Roslyn</a>.</p>
<p>How difficult could this be? Let&rsquo;s find out. There&rsquo;s some amount of documentation out there and some samples. Also, it&rsquo;s possible to generate a starter project with VS2017 that would do the file loading and minimal AST traversal. It&rsquo;s a good start, we can build on it. Here&rsquo;s a good <a href="https://github.com/dotnet/roslyn/wiki/Getting-Started-C%23-Syntax-Transformation">starting point</a> for source transformation, for example.</p>
<p>So here&rsquo;s a simplest NUnit module:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">NUnit.Framework</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">Test</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [TestFixture]</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">DumpTests</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">        [Test]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">One_plus_one_should_be_two</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="m">1</span> <span class="p">+</span> <span class="m">1</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="m">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>When converted to xUnit, it becomes this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">Xunit</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">Test</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">DumpTests</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">        [Fact]</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="k">void</span> <span class="n">One_plus_one_should_be_two</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Assert</span><span class="p">.</span><span class="n">Equal</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="m">1</span> <span class="p">+</span> <span class="m">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Precisely the following needs to be done:</p>
<ul>
<li>change <code>using</code> directive</li>
<li>remove <code>TextFixture</code> class attribute</li>
<li>replace <code>Test</code> attribute with <code>Fact</code></li>
<li>change <code>Assert.That</code> to <code>Assert.Equal</code> and swap arguments</li>
</ul>
<p>The syntax rewriter does all the work, we just need to fill in some logic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">NunitToXunitRewriter</span><span class="p">:</span> <span class="n">CSharpSyntaxRewriter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Let&rsquo;s start with the easiest, removing the <code>TextFixture</code> attribute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">NunitToXunitRewriter</span><span class="p">:</span> <span class="n">CSharpSyntaxRewriter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">override</span> <span class="n">SyntaxNode</span> <span class="n">VisitAttributeList</span><span class="p">(</span><span class="n">AttributeListSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ShouldRemoveTestFixture</span><span class="p">(</span><span class="n">node</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">base</span><span class="p">.</span><span class="n">VisitAttributeList</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Checks if the node is &#34;[TestFixture]&#34; and should be removed</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kt">bool</span> <span class="n">ShouldRemoveTestFixture</span><span class="p">(</span><span class="n">AttributeListSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">node</span><span class="p">.</span><span class="n">Attributes</span><span class="p">.</span><span class="n">Count</span> <span class="p">==</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">            <span class="p">&amp;&amp;</span> <span class="n">node</span><span class="p">.</span><span class="n">Attributes</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Name</span><span class="p">.</span><span class="n">ToString</span><span class="p">()</span> <span class="p">==</span> <span class="s">&#34;TestFixture&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&amp;&amp;</span> <span class="n">node</span><span class="p">.</span><span class="n">Parent</span> <span class="k">is</span> <span class="n">ClassDeclarationSyntax</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The code, in this case, is quite simple. The <code>VisitAttributeList</code> function gets called for every attribute in the source file. We just check that the attribute list has only one attribute, that its name is <code>TextFixture</code> and the parent node is a class. If it&rsquo;s all true, then we just return <code>null</code> from the visitor method to indicate that the node should be deleted from the syntax tree. Easy.</p>
<p>Next up is the <code>Test</code> attribute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">NunitToXunitRewriter</span><span class="p">:</span> <span class="n">CSharpSyntaxRewriter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">override</span> <span class="n">SyntaxNode</span> <span class="n">VisitAttributeList</span><span class="p">(</span><span class="n">AttributeListSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">newNode</span> <span class="p">=</span> <span class="n">TryConvertTestAttribute</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">newNode</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">newNode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">base</span><span class="p">.</span><span class="n">VisitAttributeList</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Converts &#34;[Test]&#34; to &#34;[Fact]&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">SyntaxNode</span> <span class="n">TryConvertTestAttribute</span><span class="p">(</span><span class="n">AttributeListSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">node</span><span class="p">.</span><span class="n">Attributes</span><span class="p">.</span><span class="n">Count</span> <span class="p">!=</span> <span class="m">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">node</span><span class="p">.</span><span class="n">Attributes</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Name</span><span class="p">.</span><span class="n">ToString</span><span class="p">()</span> <span class="p">!=</span> <span class="s">&#34;Test&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!(</span><span class="n">node</span><span class="p">.</span><span class="n">Parent</span> <span class="k">is</span> <span class="n">MethodDeclarationSyntax</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">            <span class="n">AttributeList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">AttributeList</span><span class="p">&lt;</span><span class="n">AttributeSyntax</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">Attribute</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Fact&#34;</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">NormalizeWhitespace</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">WithTriviaFrom</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>What we do here is quite similar to the previous example, with one exception that we&rsquo;re not deleting the node, but replacing it with something else. First, we check that it&rsquo;s a single attribute named <code>Test</code> and it&rsquo;s attached to a function. To replace it, we need to construct a new syntax node. In this case, it&rsquo;s the same thing, just the name is different. To build the syntax node we use <code>SyntaxFactory</code> methods, like <code>AttributeList</code>, <code>Attribute</code> and so on. The small quirk is the <code>NormalizeWhitespace</code> and
<code>WithTriviaFrom</code> bits. Those make sure the resulting code is formatted and has the whitespace copied from the original node. Otherwise, the output code would look out of place and would require reformatting.</p>
<p>The <code>using</code> directive change is also trivial. It&rsquo;s very similar to the <code>Fact</code> attribute situation above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">NunitToXunitRewriter</span><span class="p">:</span> <span class="n">CSharpSyntaxRewriter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">override</span> <span class="n">SyntaxNode</span> <span class="n">VisitUsingDirective</span><span class="p">(</span><span class="n">UsingDirectiveSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">newNode</span> <span class="p">=</span> <span class="n">TryConvertUsingNunit</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">newNode</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">newNode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">base</span><span class="p">.</span><span class="n">VisitUsingDirective</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Converts &#34;using NUnit.Framework&#34; to &#34;using Xunit&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">SyntaxNode</span> <span class="n">TryConvertUsingNunit</span><span class="p">(</span><span class="n">UsingDirectiveSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">node</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">ToString</span><span class="p">()</span> <span class="p">!=</span> <span class="s">&#34;NUnit.Framework&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">            <span class="n">UsingDirective</span><span class="p">(</span><span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Xunit&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">NormalizeWhitespace</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">WithTriviaFrom</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The <code>Assert</code> conversion is a much more complicated case. The problem that the expression we want to match is quite complex, even though it doesn&rsquo;t look like that. There&rsquo;s a member function access <code>Assert.That</code> and a function call <code>Assert.That(...)</code> and the argument list made up of two arguments, where the second one is a member function call as well: <code>Assert.That(actual, Is.EqualTo(expected))</code>. Using <a href="http://roslynquoter.azurewebsites.net/">Roslyn Quoter</a> tool it&rsquo;s possible to generate the code that creates such an expression:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">InvocationExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemberAccessExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">SyntaxKind</span><span class="p">.</span><span class="n">SimpleMemberAccessExpression</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Assert&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;That&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">WithArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">ArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">SeparatedList</span><span class="p">&lt;</span><span class="n">ArgumentSyntax</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">            <span class="k">new</span> <span class="n">SyntaxNodeOrToken</span><span class="p">[]{</span>
</span></span><span class="line"><span class="cl">                <span class="n">Argument</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;actual&#34;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                <span class="n">Token</span><span class="p">(</span><span class="n">SyntaxKind</span><span class="p">.</span><span class="n">CommaToken</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="n">Argument</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">InvocationExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="n">MemberAccessExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="n">SyntaxKind</span><span class="p">.</span><span class="n">SimpleMemberAccessExpression</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Is&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                            <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;EqualTo&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">                    <span class="p">.</span><span class="n">WithArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="n">ArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="n">SingletonSeparatedList</span><span class="p">&lt;</span><span class="n">ArgumentSyntax</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">                                <span class="n">Argument</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;expected&#34;</span><span class="p">))))))})))</span>
</span></span></code></pre></div><p>In the AST form this little snippet of code looks pretty huge. When we want to replace this pattern with a different piece of code, we need to find it first. And that means we need to check against the structure of every function call expression in the file and see if it&rsquo;s similar:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">NunitToXunitRewriter</span><span class="p">:</span> <span class="n">CSharpSyntaxRewriter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">override</span> <span class="n">SyntaxNode</span> <span class="n">VisitInvocationExpression</span><span class="p">(</span><span class="n">InvocationExpressionSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">newNode</span> <span class="p">=</span> <span class="n">TryConvertAssertThatIsEqualTo</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">newNode</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">newNode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">base</span><span class="p">.</span><span class="n">VisitInvocationExpression</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Converts Assert.That(actual, Is.EqualTo(expected)) to Assert.Equal(expected, actual)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">SyntaxNode</span> <span class="n">TryConvertAssertThatIsEqualTo</span><span class="p">(</span><span class="n">InvocationExpressionSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Check it&#39;s Assert.That member</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="n">IsMethodCall</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="s">&#34;Assert&#34;</span><span class="p">,</span> <span class="s">&#34;That&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// It must have exactly two arguments</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">assertThatArgs</span> <span class="p">=</span> <span class="n">GetCallArguments</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">assertThatArgs</span><span class="p">.</span><span class="n">Length</span> <span class="p">!=</span> <span class="m">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// The second argument must be a `Is.EqualTo`</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">isEqualTo</span> <span class="p">=</span> <span class="n">assertThatArgs</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">Expression</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="n">IsMethodCall</span><span class="p">(</span><span class="n">isEqualTo</span><span class="p">,</span> <span class="s">&#34;Is&#34;</span><span class="p">,</span> <span class="s">&#34;EqualTo&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// With exactly one argument</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">isEqualToArgs</span> <span class="p">=</span> <span class="n">GetCallArguments</span><span class="p">(</span><span class="n">isEqualTo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">isEqualToArgs</span><span class="p">.</span><span class="n">Length</span> <span class="p">!=</span> <span class="m">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Grab the arguments</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">expected</span> <span class="p">=</span> <span class="n">isEqualToArgs</span><span class="p">[</span><span class="m">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="kt">var</span> <span class="n">actual</span> <span class="p">=</span> <span class="n">assertThatArgs</span><span class="p">[</span><span class="m">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Build a new AST with the actual and expected nodes inserted into it</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">            <span class="n">InvocationExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">MemberAccessExpression</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SyntaxKind</span><span class="p">.</span><span class="n">SimpleMemberAccessExpression</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Assert&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">IdentifierName</span><span class="p">(</span><span class="s">&#34;Equal&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">WithArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">ArgumentList</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">SeparatedList</span><span class="p">&lt;</span><span class="n">ArgumentSyntax</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">                        <span class="k">new</span> <span class="n">SyntaxNodeOrToken</span><span class="p">[]</span> <span class="p">{</span><span class="n">expected</span><span class="p">,</span> <span class="n">Token</span><span class="p">(</span><span class="n">SyntaxKind</span><span class="p">.</span><span class="n">CommaToken</span><span class="p">),</span> <span class="n">actual</span><span class="p">})))</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">NormalizeWhitespace</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">WithTriviaFrom</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To match the expression we have to drill down into the AST and compare node by node. It&rsquo;s very tedious, but luckily after the code is written it will convert all the tests that have a similar structure. Write once, run many times. The two helper functions that are used in this matching code look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kt">bool</span> <span class="n">IsMethodCall</span><span class="p">(</span><span class="n">ExpressionSyntax</span> <span class="n">node</span><span class="p">,</span> <span class="kt">string</span> <span class="n">objekt</span><span class="p">,</span> <span class="kt">string</span> <span class="n">method</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">invocation</span> <span class="p">=</span> <span class="n">node</span> <span class="k">as</span> <span class="n">InvocationExpressionSyntax</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">invocation</span> <span class="p">==</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">memberAccess</span> <span class="p">=</span> <span class="n">invocation</span><span class="p">.</span><span class="n">Expression</span> <span class="k">as</span> <span class="n">MemberAccessExpressionSyntax</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">memberAccess</span> <span class="p">==</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="n">memberAccess</span><span class="p">.</span><span class="n">Expression</span> <span class="k">as</span> <span class="n">IdentifierNameSyntax</span><span class="p">)?.</span><span class="n">Identifier</span><span class="p">.</span><span class="n">ValueText</span> <span class="p">!=</span> <span class="n">objekt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">memberAccess</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">Identifier</span><span class="p">.</span><span class="n">ValueText</span> <span class="p">!=</span> <span class="n">method</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="n">ArgumentSyntax</span><span class="p">[]</span> <span class="n">GetCallArguments</span><span class="p">(</span><span class="n">ExpressionSyntax</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">((</span><span class="n">InvocationExpressionSyntax</span><span class="p">)</span><span class="n">node</span><span class="p">).</span><span class="n">ArgumentList</span><span class="p">.</span><span class="n">Arguments</span><span class="p">.</span><span class="n">ToArray</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In case the expression is a match, we take the <code>expected</code> and <code>actual</code> arguments, or the AST nodes that represent them, to be exact and wrap them into a different AST that represents the xUnit equivalent: <code>Assert.Equal(expected, actual)</code>.</p>
<p>Not that crazy difficult. But now we have a tool that can convert a majority of tests from NUnit to xUnit automagically. And it not only converts the <code>Assert</code> expressions but the whole file. Nice!</p>
<p>The sucky part is that the matching code is very specific to the expression we&rsquo;re trying to convert. So if we have a few variations of the <code>Assert</code> it would take writing so much code for every case. It&rsquo;s gonna very quickly get out of control. Imagine just a few very simple variations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">True</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="kc">true</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">False</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="n">Assert</span><span class="p">.</span><span class="n">That</span><span class="p">(</span><span class="n">actual</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="kc">false</span><span class="p">));</span>
</span></span></code></pre></div><p>To cover most common NUnit cases we&rsquo;d have to write hundreds of those matching functions with very repetitive code. That would be a <strong>LOT</strong> of work. Can we do better? Yes, we can! I have an idea and I&rsquo;ll describe in the next post.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In only <a href="https://gist.github.com/detunized/8d548bb3b6808f7f076ed1a5f2c6ddd4">175 lines of code</a> we have a fully functional converter that does in a second what takes a lot of time to do by hand. Even though it&rsquo;s just a proof of concept and doesn&rsquo;t cover any significant amount of NUnit assertions, I was able to convert a few files with tests with almost no additional fixing.</p>
<p><em>Also published on <a href="https://dev.to/detunized/nunit-to-xunit-automatic-test-conversion-3hc6">DEV</a> and <a href="https://medium.com/@detunized/nunit-to-xunit-automatic-test-conversion-6854588c8b03">Medium</a></em></p>
]]></content></item><item><title>Base64 decoding bug that is present in all version of .NET</title><link>https://detunized.net/posts/2019-03-06-base64-decoding-bug-that-is-present-in-all-version-of-.net/</link><pubDate>Wed, 06 Mar 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-03-06-base64-decoding-bug-that-is-present-in-all-version-of-.net/</guid><description>One sunny morning I was sitting in front of my laptop refactoring some C# code. Everything was going very smooth and it was going to be a productive day. And then I added one too many equal signs to a constant string literal and things just blew up. Gone the productivity. Gone the peaceful refactoring Sunday. Even the sun decided to hide behind the cloud.
After spending 30-40 minutes trying to figure out what I did wrong, I realized it wasn&amp;rsquo;t me.</description><content type="html"><![CDATA[<p>One sunny morning I was sitting in front of my laptop refactoring some C# code. Everything was going very smooth and it was going to be a productive day. And then I added one too many equal signs to a constant string literal and things just blew up. Gone the productivity. Gone the peaceful refactoring Sunday. Even the sun decided to hide behind the cloud.</p>
<p>After spending 30-40 minutes trying to figure out what I did wrong, I realized it wasn&rsquo;t me. It was Microsoft. Apparently, I stumbled upon an ancient bug in Base64 decoding function. This bug must be present since the introduction of <code>Convert.FromBase64String</code> in .NET 1.1 in the year of 2003. Whoa! That&rsquo;s old. And it&rsquo;s not very difficult to reproduce. Here you go:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Convert</span><span class="p">.</span><span class="n">FromBase64String</span><span class="p">(</span><span class="s">&#34;abc==&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Technically this is an illegal Base64. The legal version is <code>&quot;abc=&quot;</code>. Notice only one padding character <code>=</code>. The Base64 encoding represents every 6 bits of the binary input with one ASCII character. This means every 4 characters in the Base64 encoded string represent 3 bytes. When the encoded data is not a multiple of 3 bytes Base64 encoder adds padding characters to make the Base64 a multiple of 4 characters. This makes <code>&quot;abc=&quot;</code> a correctly padded Base64 string. Adding another <code>=</code> to it makes it invalid.</p>
<p>Base64 <code>&quot;abc=&quot;</code> decodes to two bytes <code>[105, 183]</code>. This is correct. Adding another padding character at the end shouldn&rsquo;t really change the encoded value. It&rsquo;s like adding a space at end of the sentence. Yes, it&rsquo;s there, but it doesn&rsquo;t change the meaning of the sentence. But .NET doesn&rsquo;t think so. <code>&quot;abc==&quot;</code> decodes to one byte of <code>[109]</code>. Not only it got shorter, which is weird since we made the input longer. It also got different. The first byte changed from 105 to 109. And an exception didn&rsquo;t get thrown either. Add another <code>=</code> and you&rsquo;ll get an exception. Amazing!</p>
<p><a href="https://dotnetfiddle.net/JarxXF">Code</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">Program</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">DecodeAndPrint</span><span class="p">(</span><span class="s">&#34;abc=&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">DecodeAndPrint</span><span class="p">(</span><span class="s">&#34;abc==&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="k">void</span> <span class="n">DecodeAndPrint</span><span class="p">(</span><span class="kt">string</span> <span class="n">base64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s">&#34;&#39;{0}&#39; -&gt; [{1}]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">base64</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span><span class="s">&#34;, &#34;</span><span class="p">,</span> <span class="n">Convert</span><span class="p">.</span><span class="n">FromBase64String</span><span class="p">(</span><span class="n">base64</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Output:</p>
<pre tabindex="0"><code>&#39;abc=&#39; -&gt; [105, 183]
&#39;abc==&#39; -&gt; [109]
</code></pre><p>And what is <em>really</em> amazing, is that no one discovered this for so many years. Or it got discovered, but it didn&rsquo;t get fixed. Base64 is quite fundamental in the information exchange over the wire. It is used all over the place. Yet, .NET got away with a buggy Base64 decoder for so many years.</p>
<p>At first I couldn&rsquo;t believe it and started to investigate it. I googled for a while and didn&rsquo;t really find much. Then I posted on <a href="https://stackoverflow.com/q/54852219/362938">StackOverflow</a>, but didn&rsquo;t get much luck there either. I had to even <a href="https://stackoverflow.com/a/54852796/362938">answer</a> my own question once I figured out what&rsquo;s going on. After searching on GitHub for a while I stumbled upon a <a href="https://github.com/dotnet/corefx/pull/30814">fix</a> in .NET Core made in July 2018. So the latest .NET Core version handles this correctly and throws an exception:</p>
<pre tabindex="0"><code>Unhandled Exception: System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
   at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64String(String s)
   at Program.DecodeAndPrint(String base64) in ./base64/Program.cs:line 13
   at Program.Main() in ./base64/Program.cs:line 8
</code></pre><p>It took them about 15 years to find this and fix it. What&rsquo;s interesting is that no one really tried to fix it specifically. It happened while they <a href="https://github.com/dotnet/corefx/pull/30814#issue-199014852">rewrote the code to make it faster</a>:</p>
<blockquote>
<p>Convert.FromBase64() had a subtle bug where an illicit
second padding character at the end of the string caused
the decode to &ldquo;succeed&rdquo; by dropping the fifth to
last character.</p>
</blockquote>
<blockquote>
<p>We inadvertently fixed this bug while optimizing that
api in .NetCore 2.1. Adding test to document bug and
ensure we don&rsquo;t regress.</p>
</blockquote>
<p>So this is fixed in .NET Core 2.2. But it&rsquo;s still broken in the current latest version of .NET Framework 4.7.2. And it looks like it&rsquo;s broken in <a href="https://repl.it/repls/FriendlyOnlyVerification">Mono too</a>.</p>
<p>A workaround for .NET 4.7.2 would be to repad incorrectly padded strings with something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="c1">// This only works for base64 without spaces or linebreaks.</span>
</span></span><span class="line"><span class="cl"><span class="kt">string</span> <span class="n">Repad</span><span class="p">(</span><span class="kt">string</span> <span class="n">base64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">l</span> <span class="p">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">l</span> <span class="p">%</span> <span class="m">4</span> <span class="p">==</span> <span class="m">1</span> <span class="p">&amp;&amp;</span> <span class="n">base64</span><span class="p">[</span><span class="n">l</span> <span class="p">-</span> <span class="m">1</span><span class="p">]</span> <span class="p">==</span> <span class="sc">&#39;=&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">?</span> <span class="n">base64</span><span class="p">.</span><span class="n">Substring</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">l</span> <span class="p">-</span> <span class="m">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">:</span> <span class="n">base64</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><em>Also published on <a href="https://dev.to/detunized/base64-decoding-bug-that-is-present-in-all-version-of-net-1fkp">DEV</a> and <a href="https://medium.com/@detunized/base64-decoding-bug-that-is-present-in-all-version-of-net-f53733cecdc1">Medium</a></em></p>
]]></content></item><item><title>Fuzzy search and download files with rsync in the terminal</title><link>https://detunized.net/posts/2019-03-01-fuzzy-search-and-download-files-with-rsync-in-the-terminal/</link><pubDate>Fri, 01 Mar 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-03-01-fuzzy-search-and-download-files-with-rsync-in-the-terminal/</guid><description>I use rsync a lot. I use it to copy files to USB drives, to VMs or Docker containers, to share files between computers at home, to backup stuff to remote machines or simply copy to the local Dropbox folder. And most of the time I download files from a couple of remote folders to my computer.
I do all my file operations in the terminal. I find this faster and more convenient than clicking in the GUI file manager of some sort.</description><content type="html"><![CDATA[<p>I use <code>rsync</code> <strong>a lot</strong>. I use it to copy files to USB drives, to VMs or Docker containers, to share files between computers at home, to backup stuff to remote machines or simply copy to the local Dropbox folder. And most of the time I download files from a couple of remote folders to my computer.</p>
<p>I do all my file operations in the terminal. I find this faster and more convenient than clicking in the GUI file manager of some sort. Actually I&rsquo;m so clumsy with those that I usually end up moving something important somewhere where it doesn&rsquo;t belong. Many, many years ago, when I was a young junior developer on my first job, I moved an important project on a network share to a sibling folder. All I wanted to do is to put a mouse on the other side of the keyboard and I grabbed it with my left hand. Oh, that was a major disaster: people were running around looking for the project folder, thought we are being hacked. I found it 10 minutes later.</p>
<p>Not to repeat those grave mistakes of the past, I simply stick to the command line these days. The problem with the command line though, is that it&rsquo;s not very visual. Especially when it comes to remote systems. Local file completion in <code>zsh</code> is pretty good. And once paired with a fuzzy matcher like <a href="https://github.com/junegunn/fzf"><code>fzf</code></a>, it becomes a pleasure to do almost any file operations in the terminal.</p>
<p>For a few years I relied on <code>zsh</code> remote path autocompletion for <code>rsync</code>. It works almost as good as it does for the local files. Just type some characters, press TAB and it autocompletes the filename for you. If the name is ambiguous a list of matches would pop up where you can select a name with the arrow keys. A pretty standard shell experience.</p>
<p>There&rsquo;s a problem though. Since it&rsquo;s a remote system, it takes a second or two to autocomplete. It&rsquo;s annoying. Plus it only autocompletes in the current directory and I would have to press TAB many times before I get the full path. And I can only download one file or folder at a time. Here&rsquo;s a demo. It&rsquo;s painful to watch how slow it is:</p>
<p><a href="https://asciinema.org/a/EIc6s9XzvncvUAl7DYgVuWLvp?autoplay=1"><img src="https://asciinema.org/a/EIc6s9XzvncvUAl7DYgVuWLvp.svg" alt="asciicast"></a></p>
<p>Where&rsquo;s problem, there must be a solution. And how it usually happens with the command line, the solution is a Bash script:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="nb">set</span> -eo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">HOST</span><span class="o">=</span><span class="si">${</span><span class="nv">DL_HOST</span><span class="k">:-</span><span class="nv">dev</span><span class="p">.somehost.to</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">DIR</span><span class="o">=</span><span class="si">${</span><span class="nv">DL_DIR</span><span class="k">:-</span><span class="nv">files</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;-h&#34;</span> <span class="o">||</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;--help&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;USAGE: dl [dir [host]]&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> !<span class="o">=</span> <span class="s2">&#34;&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nv">DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$2</span><span class="s2">&#34;</span> !<span class="o">=</span> <span class="s2">&#34;&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nv">HOST</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$2</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">REMOTE</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOST</span><span class="s2">:</span><span class="nv">$DIR</span><span class="s2">/&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">rsync -a <span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="p">|</span> ruby -ne <span class="s1">&#39;puts $_.split(/\s+/, 5).last&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="p">|</span> fzf -m --height 50% <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="p">|</span> rsync -avP --no-relative --files-from - <span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">&#34;</span> .
</span></span></code></pre></div><p>It works like this:</p>
<p><a href="https://asciinema.org/a/BwmSUZ7lK0mCk9Ot7797QNqQv?autoplay=1"><img src="https://asciinema.org/a/BwmSUZ7lK0mCk9Ot7797QNqQv.svg" alt="asciicast"></a></p>
<p>First it uses <code>rsync</code> to list all the files in the remote folder in the format similar to <code>ls -l</code>:</p>
<pre tabindex="0"><code>drwxr-xr-x          4,096 2019/03/01 23:23:25 src
-rw-r--r--            261 2019/02/20 23:11:49 src/PasswordManagerAccess.csproj
drwxr-xr-x          4,096 2019/03/01 23:23:25 src/Common
-rw-r--r--            379 2019/02/20 23:11:49 src/Common/BaseException.cs
-rw-r--r--            906 2019/03/01 23:23:25 src/Common/ClientException.cs
-rw-r--r--          3,454 2019/03/01 23:23:25 src/Common/Crypto.cs
-rw-r--r--            241 2019/02/20 23:11:49 src/Common/ExposeInternals.cs
-rw-r--r--          6,868 2019/03/01 23:23:25 src/Common/Extensions.cs
-rw-r--r--          1,178 2019/02/20 23:11:49 src/Common/HttpClient.cs
-rw-r--r--            487 2019/02/20 23:11:49 src/Common/IHttpClient.cs
-rw-r--r--          7,267 2019/02/20 23:11:49 src/Common/JsonHttpClient.cs
...
</code></pre><p>That gets piped into an inline Ruby script that extracts the 5th column and all the spaces it might have. And the result of that goes into <code>fzf</code> for interactive selection. Once finished, that is now finally piped into another instance of <code>rsync</code>, which is receiving a list of files on the standard input (<code>--files-from -</code>). Pretty cool. The added benefit: now I can pick multiple files easily.</p>
<p>The script takes the remote folder name and the host optionally from the command line parameters or the environment variables. The most commonly used path I decided to hardcode to push back my RSI by a couple of years.</p>
]]></content></item><item><title>Read YAMLy config with a few lines of code</title><link>https://detunized.net/posts/2019-02-25-read-yamly-config-with-a-few-lines-of-code/</link><pubDate>Mon, 25 Feb 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-02-25-read-yamly-config-with-a-few-lines-of-code/</guid><description>I was working on a C# library and in a simple example application I needed to load a config file. It didn&amp;rsquo;t have to be fancy or very efficient. Something like INI, JSON, TOML or YAML would do. What I didn&amp;rsquo;t want to have is any dependencies, not to bother the user with installing any libraries. Unfortunately, .NET doesn&amp;rsquo;t provide any of those in its standard library. There&amp;rsquo;s XML, but I cannot stomach that.</description><content type="html"><![CDATA[<p>I was working on a C# library and in a simple example application I needed to load a config file. It didn&rsquo;t have to be fancy or very efficient. Something like INI, JSON, TOML or YAML would do. What I didn&rsquo;t want to have is any dependencies, not to bother the user with installing any libraries. Unfortunately, .NET doesn&rsquo;t provide any of those in its standard library. There&rsquo;s XML, but I cannot stomach that.</p>
<p>So I though, I could probably write a simple text config file parser in a few minutes. Why not give it a try. All I need is string keys and values. Comments would be good to have. Something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># Login username</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">dude@lebowski.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># User password</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="kc">no</span><span class="w"> </span><span class="l">one will guess</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># URL</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://lebowski.com:443/index.html</span><span class="w">
</span></span></span></code></pre></div><p>This is a subset of YAML actually. Very clean and readable. How difficult would it be to write a parser for that. Normally, every time I say something like this to myself, I mentally prepare myself for a huge underestimation. What looks like a ten minute task, could turn out to be a week long project. Strangely, not this time. Thanks to pretty great runtime library and awesome LINQ support in 3 minutes I had a fully working solution:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;</span> <span class="n">ReadConfig</span><span class="p">(</span><span class="kt">string</span> <span class="n">filename</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">File</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">ReadAllLines</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">line</span> <span class="p">=&gt;</span> <span class="n">line</span><span class="p">.</span><span class="n">Trim</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">line</span> <span class="p">=&gt;</span> <span class="n">line</span><span class="p">.</span><span class="n">Length</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">line</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;#&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">line</span> <span class="p">=&gt;</span> <span class="n">line</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="k">new</span><span class="p">[]</span> <span class="p">{</span><span class="sc">&#39;:&#39;</span><span class="p">},</span> <span class="m">2</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">parts</span> <span class="p">=&gt;</span> <span class="n">parts</span><span class="p">.</span><span class="n">Length</span> <span class="p">==</span> <span class="m">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">ToDictionary</span><span class="p">(</span><span class="n">parts</span> <span class="p">=&gt;</span> <span class="n">parts</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Trim</span><span class="p">(),</span> <span class="n">parts</span> <span class="p">=&gt;</span> <span class="n">parts</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">Trim</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This function is not crazy efficient, but who cares. It&rsquo;s pretty robust, it wouldn&rsquo;t fail with an error as long as it&rsquo;s possible to read a file. It doesn&rsquo;t have any error reporting in case there&rsquo;s a syntax error, though. It would simply ignore it. In my case it&rsquo;s good enough.</p>
<p>Let&rsquo;s see how this works. First, I read the file. This call would return an array of strings, one per line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">File</span><span class="p">.</span><span class="n">ReadAllLines</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
</span></span></code></pre></div><p>Next, I trim all the whitespace on both ends. <code>Select</code> in LINQ is the same as <code>map</code> almost everywhere else, it transforms the sequence by applying a function to each element:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">line</span> <span class="p">=&gt;</span> <span class="n">line</span><span class="p">.</span><span class="n">Trim</span><span class="p">())</span>
</span></span></code></pre></div><p>Next, I filter out all lines that are blank or start with <code>#</code>. <code>Where</code> filters out the sequence by keeping the elements that satisfy the given predicate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">line</span> <span class="p">=&gt;</span> <span class="n">line</span><span class="p">.</span><span class="n">Length</span> <span class="p">&gt;</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">line</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;#&#34;</span><span class="p">))</span>
</span></span></code></pre></div><p>Next, I split each line on the first colon. If the rest of the line has more colons they will not be split and become part of the value. That&rsquo;s intentional:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">line</span> <span class="p">=&gt;</span> <span class="n">line</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="k">new</span><span class="p">[]</span> <span class="p">{</span><span class="sc">&#39;:&#39;</span><span class="p">},</span> <span class="m">2</span><span class="p">))</span>
</span></span></code></pre></div><p>Next, I filter out all the lines that didn&rsquo;t get split into exactly two parts. This is the place where syntax errors would get ignored and thrown out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="p">.</span><span class="n">Where</span><span class="p">(</span><span class="n">parts</span> <span class="p">=&gt;</span> <span class="n">parts</span><span class="p">.</span><span class="n">Length</span> <span class="p">==</span> <span class="m">2</span><span class="p">)</span>
</span></span></code></pre></div><p>And in the last step I convert the array of two element arrays to a dictionary. What in C# is called a dictionary in other languages might be called <em>object</em>, <em>map</em> or <em>hash map</em>. It&rsquo;s a key-value storage or an associative array. In this step I also trim any trailing whitespace on the key and leading whitespace on the value (other ends are trimmed already):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="p">.</span><span class="n">ToDictionary</span><span class="p">(</span><span class="n">parts</span> <span class="p">=&gt;</span> <span class="n">parts</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">TrimEnd</span><span class="p">(),</span> <span class="n">parts</span> <span class="p">=&gt;</span> <span class="n">parts</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">TrimStart</span><span class="p">());</span>
</span></span></code></pre></div><p>Done. In a few lines and one statement I&rsquo;ve read and parsed a config file.</p>
<p>JavaScript has petty similar functional programming capabilities, so it would be possible to mirror this solution in JS. <a href="https://www.destroyallsoftware.com/talks/wat">Like always</a>, there are some gotchas. In this case JS <code>String.split</code> function is acting weird. The limit parameter works differently compared to all the other languages I tried. Instead of returning the rest of the line in the last element, <code>split</code> in JavaScript truncates the input. <a href="https://www.destroyallsoftware.com/talks/wat">WAT</a>?! To fix that I have to <code>join</code> the split tail back together in the line before the final <code>reduce</code> that converts the array to object.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">readConfig</span><span class="p">(</span><span class="nx">filename</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;fs&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span> <span class="s2">&#34;utf-8&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34;\n&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">x</span> <span class="p">=&gt;</span> <span class="nx">x</span><span class="p">.</span><span class="nx">trim</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">x</span> <span class="p">=&gt;</span> <span class="nx">x</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">x</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="s2">&#34;#&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">x</span> <span class="p">=&gt;</span> <span class="nx">x</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34;:&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">x</span> <span class="p">=&gt;</span> <span class="nx">x</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">x</span> <span class="p">=&gt;</span> <span class="p">[</span><span class="nx">x</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">x</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="s2">&#34;:&#34;</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span> <span class="nx">x</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="nx">a</span><span class="p">[</span><span class="nx">x</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">trimEnd</span><span class="p">()]</span> <span class="o">=</span> <span class="nx">x</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">trimStart</span><span class="p">(),</span> <span class="nx">a</span><span class="p">),</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>JavaScript has native support for JSON, so it&rsquo;s probably stupid to roll your own config format, when JSON could be read in one short statement. The comments are not supported though.</p>
<p>I think the Ruby version is the cleanest, though it&rsquo;s practically the same:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">read_config</span> <span class="n">filename</span>
</span></span><span class="line"><span class="cl">    <span class="no">File</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">readlines</span><span class="p">(</span><span class="s2">&#34;config.yaml&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:strip</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span><span class="o">.</span><span class="n">empty?</span> <span class="o">||</span> <span class="n">x</span><span class="o">.</span><span class="n">start_with?</span><span class="p">(</span><span class="s2">&#34;#&#34;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span><span class="o">.</span><span class="n">split</span> <span class="s2">&#34;:&#34;</span><span class="p">,</span> <span class="mi">2</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span><span class="o">.</span><span class="n">size</span> <span class="o">==</span> <span class="mi">2</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="o">|</span> <span class="o">[</span><span class="n">k</span><span class="o">.</span><span class="n">rstrip</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">lstrip</span><span class="o">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">to_h</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Ruby supports both YAML and JSON out of the box. It would be easier to just do</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">YAML</span><span class="o">.</span><span class="n">load_file</span> <span class="s2">&#34;config.yaml&#34;</span>
</span></span></code></pre></div><p>but then I&rsquo;d have to quote some of the values as YAML is not that flexible with the whitespace and special characters.</p>
<p>How would I do it Go? I wouldn&rsquo;t! I don&rsquo;t want to drown in <code>if</code>s, <code>for</code>s, <code>err</code>s and <code>nil</code>s. Just say no to writing code and <code>go get</code> some packages.</p>
]]></content></item><item><title>Performance measurement of JavaScript solutions to common algorithmic questions (part 1)</title><link>https://detunized.net/posts/2019-02-21-performance-measurement-of-javascript-solutions-to-common-algorithmic-questions-part-1/</link><pubDate>Thu, 21 Feb 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-02-21-performance-measurement-of-javascript-solutions-to-common-algorithmic-questions-part-1/</guid><description>I stumbled upon a tweet and then a post by Emma Wedekind. She goes over some solutions to most common interview questions for JavaScript positions. There are three different problems. And for each she offers a brute force and an optimized solution, where the latter is a shorter and more elegant version, that normally uses functional programming style over C-style loops. I wouldn&amp;rsquo;t call them brute force and optimized though, rather naive and elegant.</description><content type="html"><![CDATA[<p>I stumbled upon a <a href="https://twitter.com/emmawedekind/status/1097855481052303360">tweet</a> and then a <a href="https://dev.to/emmawedekind/breaking-down-javascript-solutions-to-common-algorithmic-questions-part-1-1228">post</a> by <a href="https://dev.to/@emmawedekind">Emma Wedekind</a>. She goes over some solutions to most common interview questions for JavaScript positions. There are three different problems. And for each she offers a <em>brute force</em> and an <em>optimized</em> solution, where the latter is a shorter and more elegant version, that normally uses functional programming style over C-style loops. I wouldn&rsquo;t call them <em>brute force</em> and <em>optimized</em> though, rather <em>naive</em> and <em>elegant</em>.</p>
<p>The <em>naive</em> solution anyone can write as soon as they grasp the very basic concepts of the language and understand the problem. The <em>elegant</em> solution requires a better knowledge of the standard library. The code becomes clearer and more succinct. But that sometimes comes with a performance penalty that is not very obvious at the first glance. In this post I wanted to go over some of those less obvious points.</p>
<h2 id="reverse-a-string">Reverse a string</h2>
<p>Take the first problem: reverse a string. To make it simpler, let&rsquo;s first assume we have only basic ASCII or single code point UTF-16 characters in our string we want to reverse. I&rsquo;ll touch on what to do if it&rsquo;s not the case later.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">reverseString</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">reversedString</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">reversedString</span> <span class="o">+=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">reversedString</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here we append the characters from the back of the original string to the begging of an empty string one by one. This way we get the reversed string. A non obvious thing that happens here is the string reallocation. Strings in JavaScript are immutable which means every time we modify a string a copy is created. I&rsquo;m sure there are some clever optimizations to not to reallocate too much and not to create too many useless copies. But the fact is, some array has to grow and some reallocations and copying must be happening. Every time a reallocation happens some new memory gets reserved and all the bytes of the old string get copied to the new place. This takes extra time which is invisible if you look at the code. It would be best to preallocate the memory, since we know how many characters we are going to need ahead of time. As far as I know there&rsquo;s no way to do that in JavaScript.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">reverseString</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">str</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">).</span><span class="nx">reverse</span><span class="p">().</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This solution is for sure much clearer and more elegant. It&rsquo;s much easier to reason about the code. It&rsquo;s instantly obvious what the function does.</p>
<p>The hidden danger here is the additional memory the VM has to allocate to keep the temporaries. This code is roughly equivalent to the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">reverseString</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">chars</span> <span class="o">=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">reversed</span> <span class="o">=</span> <span class="nx">chars</span><span class="p">.</span><span class="nx">reverse</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">reversed</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>When it&rsquo;s written like this it becomes clear that during the execution we&rsquo;d need to keep at least two copies of the string in memory. First <code>chars</code>. It has to coexist along with the original <code>str</code>. Next <code>reversed</code>. For a short time it has to coexist along with <code>chars</code>. And then <code>result</code> has to coexist along with <code>reversed</code>. So worst case 4x the memory. Imagine now the string is 1GB long. And of the garbage collector kicks in in between some of these calls, the total run time is gonna a lot longer than it looks.</p>
<p>Here&rsquo;s some quick and dirty profiling (x-axis is string length, y-axis is milliseconds):</p>
<p><img src="https://i.imgur.com/8szFkSr.png" alt="reverse string"></p>
<p>The elegant solution here is indeed optimized. I&rsquo;m guessing that is due to all the work being done in the native functions that are dealing with the string as opposed to JavaScript code that runs on the virtual machine. And as you can see the first two solutions are not quite linear because of the intricacies of the underlying VM and CPU architecture. This would take a separate article (or a few) to explain that.</p>
<p>I briefly mentioned above the character encoding problem. Really it&rsquo;s not safe to just reverse the <code>utf-16</code> code points. There are some really complicated rules on how Unicode text is formed and some characters or graphemes could be up to 6 code points together. Look at this <a href="https://stackoverflow.com/questions/958908/how-do-you-reverse-a-string-in-place-in-javascript/16776621#16776621">answer</a> for some details. Long story short: you need to use a special library to deal with characters. Don&rsquo;t reinvent the wheel.</p>
<h2 id="the-longest-word">The longest word</h2>
<p>The second problem from the original post is about finding the length of the longest word in a string where words are separated by spaces. The original solution is to split the original string and then loop over the resulting array to find the longest string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">findLongestWordLength</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">maxVal</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">wordArr</span> <span class="o">=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">wordArr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nx">word</span> <span class="o">=</span> <span class="nx">wordArr</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">word</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="nx">maxVal</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">maxVal</span> <span class="o">=</span> <span class="nx">word</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">maxVal</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This version creates a temporary array to store all the words extracted from the original string. The words themselves also need to be stored somewhere. Since the strings are immutable it&rsquo;s very likely the actual bytes are not really duplicated but rather referenced in the original array. But that&rsquo;s hard to guess without looking at the VM source code. The fact is a bunch extra memory is needed.</p>
<p>The <em>optimized</em> version uses more functional approach:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">findLongestWordLength</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">arrOfWords</span> <span class="o">=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">arrOfLengths</span> <span class="o">=</span> <span class="nx">arrOfWords</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="p">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(...</span><span class="nx">arrOfLengths</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In this version there&rsquo;s even more memory used. <code>arrOfLengths</code> also has to be kept around. And in a situation when we have 1GB of input with only 1 letter words (I know, it&rsquo;s kinda extreme), we would have 3GB total roughly wasted on this.</p>
<p><code>Math.max</code> thing is kinda broken actually. The spread operator <code>...</code> is substituting the array elements for the function arguments. Which makes a function call with a boatload of parameters. Which in turn causes stack overflow exception with already 200k elements. Not a very big number. In <code>node</code> REPL:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(...</span><span class="nb">Array</span><span class="p">(</span><span class="mi">200000</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nx">Thrown</span><span class="o">:</span>
</span></span><span class="line"><span class="cl"><span class="nx">RangeError</span><span class="o">:</span> <span class="nx">Maximum</span> <span class="nx">call</span> <span class="nx">stack</span> <span class="nx">size</span> <span class="nx">exceeded</span>
</span></span></code></pre></div><p>For the benchmarking I fixed this version like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">findLongestWordLengthFixed</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">arrOfWords</span> <span class="o">=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">arrOfLengths</span> <span class="o">=</span> <span class="nx">arrOfWords</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="p">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">maxLength</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">arrOfLengths</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">arrOfLengths</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">&gt;</span> <span class="nx">maxLength</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">maxLength</span> <span class="o">=</span> <span class="nx">arrOfLengths</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">maxLength</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The really optimized solution would be not to create any temporary arrays, but iterate over the original string in-place and find the longest word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">findLongestWordLengthFast</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">maxLength</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">currentLength</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">l</span> <span class="o">=</span> <span class="nx">str</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">l</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">str</span><span class="p">.</span><span class="nx">charCodeAt</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span> <span class="o">===</span> <span class="mi">32</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nx">currentLength</span> <span class="o">&gt;</span> <span class="nx">maxLength</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">maxLength</span> <span class="o">=</span> <span class="nx">currentLength</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="nx">currentLength</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="o">++</span><span class="nx">currentLength</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Account for the last word
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nx">currentLength</span> <span class="o">&gt;</span> <span class="nx">maxLength</span> <span class="o">?</span> <span class="nx">currentLength</span> <span class="o">:</span> <span class="nx">maxLength</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This function does <strong>zero</strong> heap allocations and is very cache friendly. Here a quick profile (x-axis is string length, y-axis is milliseconds):</p>
<p><img src="https://i.imgur.com/P5rc9nT.png" alt="longest word"></p>
<p>As you can see the <em>brute force/naive</em> versions are performing much better than the <em>optimized/elegant</em> one.</p>
<h2 id="conclusion-sort-of">Conclusion, sort of</h2>
<p>It&rsquo;s always tempting to rewrite the code in a more elegant, cleaner and often shorter way. Sometimes it comes with a cost. It&rsquo;s important to understand that cost. Sure, when you&rsquo;re reversing a string of 3 characters any solution would do and none of this matters. But often enough we don&rsquo;t know ourselves in the beginning how big the date will be when the system hits production. Big-O analysis is important, but it&rsquo;s not everything. Those pesky constants in O(n) are important as well. Very important for real systems. They could make a big difference, like double you AWS bill or make response time 5 times larger. Keep that in mind next time you reverse a string in production =)</p>
<p>I used <a href="https://gist.github.com/detunized/17f524d74f9fd567d4be7e4a3bbaf1e6">this code</a> to profile the functions above. I used <code>node</code> on macOS. The number may vary across hardware, browsers, OSes. Keep that in mind as well.</p>
]]></content></item><item><title>DEV zen mode: userscript</title><link>https://detunized.net/posts/2019-02-19-dev-zen-mode-userscript/</link><pubDate>Tue, 19 Feb 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-02-19-dev-zen-mode-userscript/</guid><description>For a long time I&amp;rsquo;ve been killing the top and the bottom bars on Medium while reading longer articles. This is especially true on mobile, where a huge amount of precious vertical reading space is taken up by all kinds of bars. Now it seems Medium got rid of those and it got much nicer to read.
DEV has a similar problem. I&amp;rsquo;m easily distracted by the visual noise and I find it difficult to concentrate on reading when there I see something but text.</description><content type="html"><![CDATA[<p>For a long time I&rsquo;ve been killing the top and the bottom bars on Medium while reading longer articles. This is especially true on mobile, where a huge amount of precious vertical reading space is taken up by all kinds of bars. Now it seems Medium got rid of those and it got much nicer to read.</p>
<p>DEV has a similar problem. I&rsquo;m easily distracted by the visual noise and I find it difficult to concentrate on reading when there I see something but text. I can ignore the sidebar, but ignoring the horizontal bar on the bottom is difficult, especially when it cuts a line of text in the middle.</p>
<p><img src="https://i.imgur.com/bGd1T78.png" alt="Zen off"></p>
<p>So this time I decided to automate the process and make a <em>userscript</em> that removes the top, bottom and sidebars with a keyboard shortcut. Welcome <a href="https://github.com/detunized/dev-zen-mode">DEV zen mode</a>. Install it, press Shift-Z while in the article section and all the boxes go away. Press the same key again to bring them back.</p>
<p><img src="https://i.imgur.com/UOCjwkN.png" alt="Zen on"></p>
<p>To install the script you&rsquo;d need a userscript manager extension installed in your browser. That would be <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=en">Tampermonkey</a> for Chrome or <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/">Greasemonkey</a> for Firefox.</p>
<p>The script itself is available on <a href="https://openuserjs.org/scripts/detunized/DEV_Zen_mode">openuser.js</a>. Alternatively it&rsquo;s possible to create a new script with Tampermonkey/Greasemonkey and paste the file form <a href="https://github.com/detunized/dev-zen-mode/blob/master/dev-zen-mode.user.js">GitHub</a> into it.</p>
<p>The core of the feature was not that difficult to put together. Adding a keyboard shortcut and hiding some elements is pretty trivial with vanilla JavaScript. I&rsquo;m sure the code is not very robust yet and could benefit from some cleaning up. For one thing, I&rsquo;m not saving the original <code>display</code> property, just assuming it&rsquo;s blank. It works not, but might get broken when CSS changes.</p>
<p>There was one problem I ran into though. I wanted to be able to bring back the hidden elements when the user navigates away from the page. To make this happen I tried to find an event that is fired when the URL changes. To my surprise everything I found online didn&rsquo;t work. I tried to add a listener for <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onhashchange"><code>hashchange</code> event</a>, but couldn&rsquo;t get any callbacks to trigger. After a while I gave up and used some <a href="https://stackoverflow.com/a/18950690/362938">hack</a> I found on StackOverflow.</p>
<p>I would really love to see this becoming a feature of DEV. It doesn&rsquo;t have to be exactly like this, but some kind of a reading mode would be really nice to have. I&rsquo;m not a web developer and I don&rsquo;t think I have the chops to contribute a feature like this to the codebase. Anyone interested? =)</p>
]]></content></item><item><title>Git-Fu: reposurgeon</title><link>https://detunized.net/posts/2019-02-17-git-fu-reposurgeon/</link><pubDate>Sun, 17 Feb 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-02-17-git-fu-reposurgeon/</guid><description>In a comment to my previous post @610yesnolovely mentioned a tool called reposurgeon that is supposed to take care of the exact problem I had (merging a bunch of Git repos). I installed it and tried it out. I won&amp;rsquo;t keep you in suspense here, it didn&amp;rsquo;t work for me.
I spent a bunch of time going through the giant documentation page, which seems to be both detailed and vague at the same time.</description><content type="html"><![CDATA[<p>In a <a href="https://dev.to/610yesnolovely/comment/8nk8">comment</a> to my previous <a href="https://detunized.net/posts/2019-02-15-git-fu-merge-multiple-repos-with-linear-history/">post</a> <a href="https://dev.to/610yesnolovely">@610yesnolovely</a> mentioned a tool called <a href="https://gitlab.com/esr/reposurgeon">reposurgeon</a> that is supposed to take care of the exact problem I had (merging a bunch of Git repos). I installed it and tried it out. I won&rsquo;t keep you in suspense here, it didn&rsquo;t work for me.</p>
<p>I spent a bunch of time going through the giant <a href="http://www.catb.org/~esr/reposurgeon/reposurgeon.html">documentation page</a>, which seems to be both detailed and vague at the same time. It feels too formal and yet doesn&rsquo;t give you a good idea what the whole thing is about. There&rsquo;s a lot of terminology that is unique to this tool and not a lot of introduction into the lingo. There are very few examples of anything. No quick start section with trivial cases covered. Quite difficult to figure out where to start. I also didn&rsquo;t find a lot of resources online. Hardly any, actually. Not many people seem to use this tool.</p>
<p>I&rsquo;m sure reposurgeon is a beast. It seems to have more commands and switches than <code>git</code>, <code>openssl</code> and <a href="https://imagemagick.org/script/mogrify.php"><code>mogrify</code></a> combined. It has a query language that makes Oracle green with envy. But for the life of me, I couldn&rsquo;t figure out how to use any of it. It&rsquo;s just not intuitive at all. Some commands are pretty arbitrary, like <code>append</code>, that lets you append text to the commit message. Yet, there&rsquo;s no <code>prepend</code>.</p>
<p>The query language is quite obscure and doesn&rsquo;t resemble anything I&rsquo;m familiar with. Here&rsquo;s an example from the docs:</p>
<pre tabindex="0"><code>define lastchange {
    @max(=B &amp; [/ChangeLog/] &amp; /{0}/B)? list
}
</code></pre><p>And the description:</p>
<blockquote>
<p>List the last commit that refers to a ChangeLog file containing a specified string. (The trick here is that ? extends the singleton set consisting of the last eligible ChangeLog blob to its set of referring commits, and listonly notices the commits.)</p>
</blockquote>
<p>That makes zero sense to me, though I&rsquo;ve spent quite a bit of time trying to understand the docs.</p>
<p>To me this looks like a well run project. It seems to have good <a href="http://www.catb.org/~esr/reposurgeon/NEWS">pace of releases</a>, vast and detailed <a href="http://www.catb.org/esr/reposurgeon/">documentation</a>, clean and documented source code (I wouldn&rsquo;t put everything in <a href="https://gitlab.com/esr/reposurgeon/blob/master/src/goreposurgeon/goreposurgeon.go">one 21k line file</a> though).</p>
<p>What I think went wrong is that it&rsquo;s got too many very specific features and it tries to handle everything. Probably the author(s) have been spending too much time in their silo perfecting and adding features to their software.</p>
<p>I think they should have been spending more time on Stack Overflow answering questions about reposurgeon, writing introduction to the tool and blogging about it. I understand this tool is pretty niche and is designed for some very special use cases, but that should not mean &ldquo;experts only&rdquo;. A casual user should not be intimidated by the advanced features right away. It should be possble to do a trivial conversion with some minimal command set.</p>
<p>Look at Git. I know it&rsquo;s a train wreck of a user experience. It&rsquo;s got commands for everything. Mastering Git could take a lifetime. But if you stick to <code>pull-add-commit-push</code> workflow, it&rsquo;s not that bad. Powerful, shouldn&rsquo;t mean unapproachable.</p>
]]></content></item><item><title>Git-Fu: merge multiple repos with linear history</title><link>https://detunized.net/posts/2019-02-15-git-fu-merge-multiple-repos-with-linear-history/</link><pubDate>Fri, 15 Feb 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-02-15-git-fu-merge-multiple-repos-with-linear-history/</guid><description>The other day I invented myself a new headache: I wanted to merge a few libraries I&amp;rsquo;ve built over the years into one repo and refactor them together. It looks simple at first glance, copy all the files into subfolders, add and commit:
$ git add . $ git commit -m &amp;#39;All together now!&amp;#39; Done!
No, not really. This would eliminate the history. And I really wanted to keep it. I often go back to see what changes have been made to a file, I use blame to see when and why certain code was modified.</description><content type="html"><![CDATA[<p>The other day I invented myself a new headache: I wanted to merge a few libraries I&rsquo;ve built over the years into one repo and refactor them together. It looks simple at first glance, copy all the files into subfolders, add and commit:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ git add .
</span></span><span class="line"><span class="cl">$ git commit -m <span class="s1">&#39;All together now!&#39;</span>
</span></span></code></pre></div><p>Done!</p>
<p>No, not really. This would eliminate the history. And I really wanted to keep it. I often go back to see what changes have been made to a file, I use blame to see when and why certain code was modified. I looked around to see how to get it done quickly. I found a whole bunch blog posts describing similar undertakings. I also found code snippets, shell and Python scripts and even Java programs to do just that.</p>
<p>After trying some of those (not the Java though, no thank you) I realized they don&rsquo;t do exactly what I want. Some of the authors tried to keep the original commit hashes. Some authors wanted to have the original file paths. I wanted to be able to track changes from the beginning of the history.</p>
<p>Most of the approaches I found were not compatible with my goals. Usually people try to import a repo into a branch (usually by adding a <code>remote</code> from another repo), move all the files into a subfolder in one commit and then merge that branch into <code>master</code>. This creates one big commit where all the files get moved to a subfolder. And then another giant merge commit, where all the changes from one branch get copied to another branch. When you view such a repo on GitHub, you&rsquo;d see that file history gets broken (<code>blame</code> still works though).</p>
<p>I also discovered a built-in command <code>git subtree</code> and it turns out it suffers from the same problems as all the third party tools I tried before that. So no go! Need to reinvent the wheel here and come up with my own solution.</p>
<p>So, basically, I needed to find a way to merge all the repos without creating any merge commits. And I needed to move the original files into subfolders. Two Git tools come to mind: <code>cherry-pick</code> and <code>filter-branch</code>.</p>
<p>A sidenote. I used to use Mercurial at work a few years back and it was great! The user experience on Mercurial is amazing. I kinda wish it didn&rsquo;t die a slow death and let the inferior product to take over the dev scene. As Mercurial was intuitive and user friendly as Git is powerful and versatile. Git is like a really twisted Lego set: you can build whatever you want out of it.</p>
<p>So here&rsquo;s the plan:</p>
<ul>
<li>put each repo in its own branch</li>
<li>rewrite the history to move all the files in each commit into a subfolder</li>
<li>rewrite the history to prepend the repo name to the commit message</li>
<li>cherry pick all the commits from all the branches in chronological order into <code>master</code></li>
<li>delete branches</li>
<li>garbage collect to shrink the repo</li>
</ul>
<p>Easy peasy. Feel kinda masochistic today, so let&rsquo;s do in Bash.</p>
<p>First, like always we need to make sure the whole script fails when any command fails. This usually saves a lot of time when something goes wrong. And it usually does.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="nb">set</span> -euo pipefail
</span></span></code></pre></div><p>List of repos I&rsquo;d like to join:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">repos</span><span class="o">=</span><span class="s2">&#34;1password bitwarden dashlane lastpass opvault passwordbox roboform stickypassword truekey zoho-vault&#34;</span>
</span></span></code></pre></div><p>Make sure we start form scratch:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rm -rf joined
</span></span><span class="line"><span class="cl">git init joined
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> joined
</span></span></code></pre></div><p>Now, here&rsquo;s a tough one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">for</span> repo in <span class="nv">$repos</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    git remote add <span class="nv">$repo</span> <span class="nv">$REPO_DIR</span>/repo
</span></span><span class="line"><span class="cl">    git fetch <span class="nv">$repo</span>
</span></span><span class="line"><span class="cl">    git checkout -b <span class="nv">$repo</span> <span class="nv">$repo</span>/master
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> -n <span class="s2">&#34;</span><span class="nv">$repo</span><span class="s2">: &#34;</span> &gt; prefix
</span></span><span class="line"><span class="cl">    git filter-branch <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        -f <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --tree-filter <span class="s2">&#34;mkdir -p .original/</span><span class="nv">$repo</span><span class="s2"> &amp;&amp; rsync -a --remove-source-files ./ .original/</span><span class="nv">$repo</span><span class="s2">/&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --msg-filter <span class="s2">&#34;cat </span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span><span class="s2">/prefix -&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --env-filter <span class="s1">&#39;GIT_COMMITTER_DATE=&#34;$GIT_AUTHOR_DATE&#34;&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        <span class="nv">$repo</span>
</span></span><span class="line"><span class="cl">    rm prefix
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span></code></pre></div><p>Let&rsquo;s go over it piece by piece. First, I import a repo into its own branch. The repo named <code>lastpass</code> end up in a branch named <code>lastpass</code>. Nothing difficult so far.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git remote add <span class="nv">$repo</span> <span class="nv">$REPO_DIR</span>/repo
</span></span><span class="line"><span class="cl">git fetch <span class="nv">$repo</span>
</span></span><span class="line"><span class="cl">git checkout -b <span class="nv">$repo</span> <span class="nv">$repo</span>/master
</span></span></code></pre></div><p>In the next step I rewrite the history for each repo to move files into a subfolder for each commit. For example, all the files coming from the repo <code>lastpass</code> would end up in the <code>.original/lastpass/</code> folder. And it would be changed for all the commits in the history, like all the development was done inside this folder and not at the root.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git filter-branch <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -f <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --tree-filter <span class="s2">&#34;mkdir -p .original/</span><span class="nv">$repo</span><span class="s2"> &amp;&amp; rsync -a --remove-source-files ./ .original/</span><span class="nv">$repo</span><span class="s2">/&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --msg-filter <span class="s2">&#34;cat </span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span><span class="s2">/prefix -&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --env-filter <span class="s1">&#39;GIT_COMMITTER_DATE=&#34;$GIT_AUTHOR_DATE&#34;&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nv">$repo</span>
</span></span></code></pre></div><p>The <code>filter-branch</code> command is a multifunctional beast. It&rsquo;s possible to change the repo beyond any recognition with all the possible switches it provides. It&rsquo;s possible to FUBAR it too. Actually it&rsquo;s super easy. That&rsquo;s why Git creates a backup under <code>refs/original/refs/heads</code> branch. To force the the backup to be overwritten if it&rsquo;s already there I use the <code>-f</code> switch.</p>
<p>When the <code>--tree-filter</code> switch is used, every commit is checked out to a temporary directory and using regular file operations I can rewrite the commit. So for every commit, I create a directory <code>.original/$repo</code> and move all the file into it using <code>rsync</code>.</p>
<p>The <code>--mag-filter</code> switch allows me to rewrite the commit message. I&rsquo;d like to add the repo name to the message, so that all the commits that are coming from the <code>lastpass</code> repo would look like <code>lastpass: original commit message</code>. For each commit the script would receive the commit message on <code>stdin</code> and whatever comes out to <code>stdout</code> would become the new commit message. In this case I use <code>cat</code> to join <code>prefix</code> and <code>stdin</code>(<code>-</code>). For some reason I couldn&rsquo;t figure out why simple <code>echo -n</code> wouldn&rsquo;t work, so I had to save the message prefix into a file.</p>
<p>And the last bit with <code>--env-filter</code> is needed to reset the commit date to the original date (author date in Git terminology). If I didn&rsquo;t do it, Git would change the timestamp to the current time. I didn&rsquo;t want that.</p>
<p>Next step would be to copy all those commits to the <code>master</code> branch to flatten the history. There&rsquo;s no <code>master</code> branch yet. Let&rsquo;s make one. For some reason Git creates a branch with all the files added to the index. Kill them with <code>git rm</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout --orphan master
</span></span><span class="line"><span class="cl">git rm -rf .
</span></span></code></pre></div><p>To copy the commits, I need to list them first. That is done with the <code>log</code> command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log --pretty<span class="o">=</span><span class="s1">&#39;%H&#39;</span> --author-date-order --reverse <span class="nv">$repos</span>
</span></span></code></pre></div><p>This command produces a list of all the commit hashes sorted from the oldest to the newest across all the branches I created earlier. The output of this step looks like this:</p>
<pre tabindex="0"><code>7d62b1272b4aa37f07eb91bbf46a33609d80155f
a8673683cb13a2040299dcb9c98a6f1fcb110dbd
f3876d3a4900e7f6012efeb0cc06db241b0540d6
7209ecf519475e59494504ca2a75e36ad9ea6ebe
</code></pre><p>Now that I have the list, I iterate and <code>cherry-pick</code> each commit into <code>master</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">for</span> i in <span class="k">$(</span>git log --pretty<span class="o">=</span><span class="s1">&#39;%H&#39;</span> --author-date-order --reverse <span class="nv">$repos</span><span class="k">)</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nv">GIT_COMMITTER_DATE</span><span class="o">=</span><span class="k">$(</span>git log -1 --pretty<span class="o">=</span><span class="s1">&#39;%at&#39;</span> <span class="nv">$i</span><span class="k">)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        git cherry-pick <span class="nv">$i</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span></code></pre></div><p>The <code>GIT_COMMITTER_DATE</code> environment variable is again used to reset the commit date to the original creation time, which I get with the <code>log</code> command again like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log -1 --pretty<span class="o">=</span><span class="s1">&#39;%at&#39;</span> &lt;COMMIT-HASH&gt;
</span></span></code></pre></div><p>After these steps I have a repo with flat history, where each original repo lives in its own subdirectory under <code>.original/</code>. I can use GitHub file history and blame to see all the changes that happened to the original files since their birth. And since Git tracks renames I could just move these files to their new home inside the megarepo and I would still get the history and blame working.</p>
<p>The only thing left to do is to clean up the repo, delete all the branches I don&rsquo;t need anymore and run the garbage collector to take out the trash.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">for</span> repo in <span class="nv">$repos</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    git branch -D <span class="nv">$repo</span>
</span></span><span class="line"><span class="cl">    git remote remove <span class="nv">$repo</span>
</span></span><span class="line"><span class="cl">    git update-ref -d refs/original/refs/heads/<span class="nv">$repo</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git gc --aggressive
</span></span></code></pre></div><p>The resulting repo lives <a href="https://github.com/detunized/password-manager-access">here</a>. I feel like it was worth the time I put into that. It gave me an opportunity to learn more about the Git low level commands. And now I have a repo that I can browse with ease and don&rsquo;t need to jump between the branches every time I want to check some file history.</p>
<p>The script I used could be found <a href="https://gist.github.com/detunized/7c41718863ab94e7072f99a55a5bf9d4">here</a>.</p>
]]></content></item><item><title>Giving Go another chance: error handling</title><link>https://detunized.net/posts/2019-02-12-giving-go-another-chance-error-handling/</link><pubDate>Tue, 12 Feb 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-02-12-giving-go-another-chance-error-handling/</guid><description>Since the last episode I managed to add a few features here and there: ability to delete entries, minimal editing, some improvements on the file storage. It was quite difficult to find any time to get anything meaningful done, unfortunately. Life happened. It was quite hectic and busy. I had time to think though. I realized I didn&amp;rsquo;t pick the best project to learn Go with.
First of all, I&amp;rsquo;m trying to recreate a piece of software that I&amp;rsquo;m mostly happy with.</description><content type="html"><![CDATA[<p>Since the last episode I managed to add a few features here and there: ability to delete entries, minimal editing, some improvements on the file storage. It was quite difficult to find any time to get anything meaningful done, unfortunately. Life happened. It was quite hectic and busy. I had time to think though. I realized I didn&rsquo;t pick the best project to learn Go with.</p>
<p>First of all, I&rsquo;m trying to recreate a piece of software that I&rsquo;m mostly happy with. I&rsquo;d say 99%. I realized that while using it more and more. I should have rather made some pull requests to the original project instead of trying to rewrite it. But then I wouldn&rsquo;t learn Go, would I?</p>
<p>Second, Go doesn&rsquo;t seem to be a good fit for this kind of project. Text processing, no concurrency, no network access. Writing some sort of server or network crawler in Go would be a better fit. I&rsquo;ll try something like this next.</p>
<p>So far I could say things mainly went well. It was not difficult to get productive in a matter of a couple of days. Writing Go feels like writing C most of the time without some of the C headaches. I keep typing the types first, though, and then wonder why it doesn&rsquo;t compile. C habits die slow.</p>
<p>The thing that tripped me up all the time is the error handling. Most often peope complain about the necessity to type <code>if err != nil ...</code> after every function call. I&rsquo;d say it doesn&rsquo;t even bother me that much. It makes error handling explicit with a clear control flow path, versus exception-like implicit secondary control flow.</p>
<p>I think the most difficult part for me is that there&rsquo;s no idiomatic way of dealing with errors. The error itself is too generic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="kt">error</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That&rsquo;s it. Just something that returns a string. So anything could be an error. And most of the time it&rsquo;s just a string. For example, <code>os.IsNotExist</code>. It checks if the error returned from any <code>file.*</code> function means that file wasn&rsquo;t found:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">IsNotExist</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">isNotExist</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In most systems I know of the errors are encoded as enumerations, types or a combination of the two. Something the compiler can actually work with. In Go it&rsquo;s often enough just a string created with <code>fmt.Errorf</code>. Handling an error like this is difficult and feels kinda dirty, because the error message <em>is</em> the error value and its encoding. This is how it&rsquo;s handled in the standard <code>os</code> package:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">isNotExist</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">checkErrMessageContent</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="s">&#34;does not exist&#34;</span><span class="p">,</span> <span class="s">&#34;not found&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;has been removed&#34;</span><span class="p">,</span> <span class="s">&#34;no parent&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">checkErrMessageContent</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">,</span> <span class="nx">msgs</span> <span class="o">...</span><span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">err</span> <span class="p">=</span> <span class="nf">underlyingError</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">msgs</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nf">contains</span><span class="p">(</span><span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">(),</span> <span class="nx">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>So to see if the file didn&rsquo;t open because it doesn&rsquo;t exist, I&rsquo;d have to check the error message for &ldquo;does not exist&rdquo; or &ldquo;not found&rdquo;. What if it&rsquo;s &ldquo;doesn&rsquo;t exist&rdquo; or if it&rsquo;s something else that wasn&rsquo;t found? Luckily there are convenience methods provided for this purpose: <code>os.IsExist</code> and <code>os.IsNotExist</code>. Nice!</p>
<p>This means to handle my own errors, I&rsquo;d have to create similar methods if I don&rsquo;t want to compare strings all over my program. That&rsquo;s kinda tedious.</p>
<p>Another approach is to create my own error types. Which is also done in many places in the standard lib. At least is then possible to have a <a href="https://tour.golang.org/methods/15">type assertion</a> to see what type of error I got:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">underlyingError</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">err</span><span class="p">.(</span><span class="kd">type</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="o">*</span><span class="nx">PathError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">err</span><span class="p">.</span><span class="nx">Err</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="o">*</span><span class="nx">LinkError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">err</span><span class="p">.</span><span class="nx">Err</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="o">*</span><span class="nx">SyscallError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">err</span><span class="p">.</span><span class="nx">Err</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Often enough when the error is handled it&rsquo;s wrapped into another error by simply gluing the error messages together. It&rsquo;s a normal thing to get this from a Go program:</p>
<pre tabindex="0"><code>Error: Error building site: failed to render pages: render of &#34;page&#34; failed: execute of template failed: template: _internal/opengraph.html:31:19: executing &#34;_internal/opengraph.html&#34; at &lt;.&gt;: range can&#39;t iterate over Making a time tracking tool in Go
</code></pre><p>Notice how many colons (<code>:</code>) are in that error message. Practically each <code>:</code> is a callstack entry, because the typical pattern is to handle an error and wrap it again:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nf">blah</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;blah failed: %v&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>There&rsquo;s a library <a href="https://github.com/pkg/errors">pkg/errors</a> to handle this in a bit better way:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nf">blah</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">Wrap</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="s">&#34;blah failed&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>It&rsquo;s a step forward. A very small one, but a step forward. The errors are still strings, but they have some idea of structure. Handling such errors is still painful. So far I opted not to handle them properly, just pass along to the poor user to deal with.</p>
<p>To summarize: I have not yet gotten a good feeling on how to return and handle errors in Go. I&rsquo;ll experiment more and see what works for me better. I wish there was something like <a href="https://doc.rust-lang.org/std/result/"><code>std::result</code></a> in Rust or <a href="https://www.scala-lang.org/api/2.12.0/scala/util/Try.html"><code>Try</code></a> in Scala.</p>
<hr>
<ul>
<li>Total time spent: about 20 hours</li>
</ul>
]]></content></item><item><title>Blog With Hugo</title><link>https://detunized.net/posts/2019-02-02-blog-with-hugo/</link><pubDate>Sat, 02 Feb 2019 14:25:41 +0100</pubDate><guid>https://detunized.net/posts/2019-02-02-blog-with-hugo/</guid><description>I decided to resurrect my personal blog and not rely solely on dev.to for my blogging needs. Though, it&amp;rsquo;s really pleasant to use dev.to for technical blogging, some aspects of it could be better. I don&amp;rsquo;t like so much to type all my posts in the browser and would rather use a proper text editor. I also don&amp;rsquo;t like to switch between preview and markdown view all the time. And I especially don&amp;rsquo;t like to have no version control for my content.</description><content type="html"><![CDATA[<p>I decided to resurrect my personal blog and not rely solely on <a href="https://dev.to/detunized">dev.to</a> for my blogging needs. Though, it&rsquo;s really pleasant to use dev.to for technical blogging, some aspects of it could be better. I don&rsquo;t like so much to type all my posts in the browser and would rather use a proper text editor. I also don&rsquo;t like to switch between preview and markdown view all the time. And I especially don&rsquo;t like to have no version control for my content.</p>
<p>So far I&rsquo;ve been just editing offline and pasting into the browser every 5 minutes to see what it looks like. When I&rsquo;m done, I commit and then paste the final version and publish. When I find a typo, I fix, commit and publish again. I wanted to have a slightly different workflow.</p>
<p>I used to have a Jekyll blog and it was nice overall, but I had some gripes with it. I wanted to try something else. I was sure many new exciting tools came out while I wasn&rsquo;t looking. And after I looked&hellip; I was shocked and overwhelmed with choice. Hugo, Hexo, Gatsby, Next and more. There&rsquo;s even a dedicated <a href="https://www.staticgen.com/">website</a> to track their GitHub stars, forks and other stuff. Wow!</p>
<p>And this is not it. I&rsquo;d have to find a theme as well. Each of those projects has a theme shop or two. After browsing some of them and reading a bunch about what is better, easier, more powerful or what have you, I decided to go with Hugo and some theme I picked.</p>
<p>What was cool about Hugo, that it&rsquo;s a one stop shop. It processes everything, from Markdown down to the compiled minified resources for my site. No extra plugins needed. Installing it was super easy. Setting up the blog was even easier:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ hugo new site blog
</span></span></code></pre></div><p>Done!</p>
<p>Adding a theme was not much more difficult:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ hub clone Track3/hermit themes/hermit
</span></span></code></pre></div><p>Then I deployed that on <a href="https://netlify.com">Netlify</a>. And it was easy too. Just tell Netlify to use the GitHub repo and publish it with Hugo. Now when I push to GitHub, Netlify picks it up, builds it and it&rsquo;s live in under a minute. Amazing!</p>
<p>Can it be all sunshine and rainbows? Sure not. The shit started happening after that. First, I tried to fix myself some CSS. I&rsquo;m not sure how we as humanity arrived to this technology. I guess, one wrong wrong turn here, another wrong turn there and now we&rsquo;re stuck with this mess. Have you ever tried to center a <code>div</code>?</p>
<p><img src="https://i.imgur.com/sEOwTQo.jpg" alt="People who know how to center a div"></p>
<p>Luckily I didn&rsquo;t need to do that. But I wanted to fix some minor annoyances. I spent way too many hours on that, along with creating pull requests to the original theme and trying to fix the git repo on multiple occasions. Pro tip: use a new branch for each pull request.</p>
<p>Another major breakdown happened with Netlify not picking up my changes in the theme git submodule. I spent again way too many hours tracking this down. I even emailed the support, and they were very responsive and helpful, though they didn&rsquo;t really help with the actual problem. It wasn&rsquo;t obvious. After screwing around for few more hours I arrived at a solution: <strong>put generated CSS into the repo and commit after each change</strong>.</p>
<p>Apparently, Netlify <a href="https://github.com/netlify/build-image/issues/182">doesn&rsquo;t support Hugo pipelines</a> at the moment. Which means the generated folder <code>resources</code> must be included in the repo as a workaround. Since the theme I picked had that in the repo, Hugo was just copying it to the <code>public</code> folder and was done with it, skipping all the SCSS processing on the server. Silly me, trying to fix that CSS.</p>
<p>If some poor soul is reading this and has the same problem: commit your top level <code>resources</code> folder into the repo.</p>
<p><a href="https://github.com/fool">Criss</a> from Netlify wrote to me that I can <a href="https://github.com/netlify/build-image/issues/254">join the beta</a> and have my SCSS build on the server. I&rsquo;m gonna give it a try a bit later. Now I want to enjoy stuff working for a little bit.</p>
<p>The preview of my new blog is <a href="https://feed-dead-beef.netlify.com/">here</a>. It&rsquo;s still got many problems and doesn&rsquo;t have a proper home yet. I also would like to merge it with my dormant <a href="https://detunized.net">photo blog</a> before I make it public. There&rsquo;s always twice as much work still to do, no matter how much is done already.</p>
]]></content></item><item><title>Giving Go another chance: display command</title><link>https://detunized.net/posts/2019-01-28-giving-go-another-chance-display-command/</link><pubDate>Mon, 28 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-28-giving-go-another-chance-display-command/</guid><description>So far I got only to C in my CRUD application. It&amp;rsquo;s time to move on to R. I wanted to display a list of entries in the log similar to what timetrap does:
Id Day Start End Duration Notes 40 Sun Jan 20, 2019 22:00:00 - 01:00:00 3:00:00 initial setup 3:00:00 41 Mon Jan 21, 2019 22:37:23 - 00:37:26 2:00:03 cli and cobra 2:00:03 43 Tue Jan 22, 2019 11:40:14 - 12:01:56 0:21:42 tags 44 12:02:08 - 12:34:00 0:31:52 parse tags from the comment 0:53:34 48 Wed Jan 23, 2019 00:57:40 - 01:34:35 0:36:55 out command 49 01:34:53 - 02:06:28 0:31:35 fix tag parsing 1:08:30 59 Sat Jan 26, 2019 21:52:17 - 22:42:48 0:50:31 sqlite 60 23:37:25 - 00:11:05 0:33:40 sqlite 1:24:11 61 Sun Jan 27, 2019 15:12:51 - 15:29:04 0:16:13 sqlite 0:16:13 62 Mon Jan 28, 2019 00:13:13 - 00:47:54 0:34:41 sqlite 63 22:30:26 - 22:53:47 0:23:21 backend 64 23:24:25 - 00:37:21 1:12:56 backend 2:10:58 66 Tue Jan 29, 2019 11:19:19 - 11:20:36 0:01:17 display 67 11:24:10 - 11:42:07 0:17:57 display 68 11:51:51 - 11:52:53 0:01:02 display 69 12:57:04 - 13:01:14 0:04:10 display 70 13:10:37 - 14:03:54 0:53:17 display 71 15:00:21 - 15:31:03 0:30:42 display 72 21:11:52 - 21:16:23 0:04:31 display 73 21:16:29 - 23:18:54 2:02:25 display 3:55:21 74 Wed Jan 30, 2019 18:38:29 - 18:43:57 0:05:28 ids 75 20:27:47 - 21:42:52 1:15:05 ids 1:20:33 ------------------------------------------------------- Total 16:09:23 This, by the way, is how much time I&amp;rsquo;ve dedicated so far to this project.</description><content type="html"><![CDATA[<p>So far I got only to <strong>C</strong> in my CRUD application. It&rsquo;s time to move on to <strong>R</strong>. I wanted to display a list of entries in the log similar to what <a href="https://github.com/samg/timetrap">timetrap</a> does:</p>
<pre tabindex="0"><code>Id  Day                Start      End        Duration   Notes
40  Sun Jan 20, 2019   22:00:00 - 01:00:00   3:00:00    initial setup
                                             3:00:00
41  Mon Jan 21, 2019   22:37:23 - 00:37:26   2:00:03    cli and cobra
                                             2:00:03
43  Tue Jan 22, 2019   11:40:14 - 12:01:56   0:21:42    tags
44                     12:02:08 - 12:34:00   0:31:52    parse tags from the comment
                                             0:53:34
48  Wed Jan 23, 2019   00:57:40 - 01:34:35   0:36:55    out command
49                     01:34:53 - 02:06:28   0:31:35    fix tag parsing
                                             1:08:30
59  Sat Jan 26, 2019   21:52:17 - 22:42:48   0:50:31    sqlite
60                     23:37:25 - 00:11:05   0:33:40    sqlite
                                             1:24:11
61  Sun Jan 27, 2019   15:12:51 - 15:29:04   0:16:13    sqlite
                                             0:16:13
62  Mon Jan 28, 2019   00:13:13 - 00:47:54   0:34:41    sqlite
63                     22:30:26 - 22:53:47   0:23:21    backend
64                     23:24:25 - 00:37:21   1:12:56    backend
                                             2:10:58
66  Tue Jan 29, 2019   11:19:19 - 11:20:36   0:01:17    display
67                     11:24:10 - 11:42:07   0:17:57    display
68                     11:51:51 - 11:52:53   0:01:02    display
69                     12:57:04 - 13:01:14   0:04:10    display
70                     13:10:37 - 14:03:54   0:53:17    display
71                     15:00:21 - 15:31:03   0:30:42    display
72                     21:11:52 - 21:16:23   0:04:31    display
73                     21:16:29 - 23:18:54   2:02:25    display
                                             3:55:21
74  Wed Jan 30, 2019   18:38:29 - 18:43:57   0:05:28    ids
75                     20:27:47 - 21:42:52   1:15:05    ids
                                             1:20:33
    -------------------------------------------------------
    Total                                   16:09:23
</code></pre><p>This, by the way, is how much time I&rsquo;ve dedicated so far to this project. And all those tiny little bits of time I get to work on this between dealing with small kids, changing diapers, feeding them and putting out other fires at home. Talking about programmer productivity and <a href="https://heeris.id.au/2013/this-is-why-you-shouldnt-interrupt-a-programmer/">interruptions</a>.</p>
<p>This feature turned from a quick one into a time sucking endeavor. First the <code>Backend</code> interface had to be changed to support retrieval of completed and open entries.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Backend ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Backend</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">OpenEntry</span><span class="p">(</span><span class="nx">comment</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">startTime</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">,</span> <span class="nx">tags</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">    <span class="nf">CloseEntry</span><span class="p">(</span><span class="nx">endTime</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">)</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">GetEntries</span><span class="p">()</span> <span class="p">([]</span><span class="nx">Entry</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GetOpenEntry</span><span class="p">()</span> <span class="p">(</span><span class="nx">Entry</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Oh, the error handling! Why not just have <code>error</code> a part of <strong>every</strong> function in Go? I&rsquo;ll talk about this in a separate post. So far I could say I&rsquo;m happy with the simplistic nature of Go. The only thing I&rsquo;m suffering from so far is the error handling. <code>if err != nil { return err }</code>.</p>
<p>My database format now spans three different files. <code>entries.json</code> to store the entries themselves. <code>current.json</code> to store the currently open entry, which is later moved to <code>entries.json</code> once it&rsquo;s closed. And <code>id.json</code> which stores the last used index. I&rsquo;m starting to regret the decision not to use SQLite, which would provide me with all that plumbing and free up some time to learn and google SQL queries. For now I&rsquo;m also ignoring the problem of preventing two instances of the program trying to modify the database at the same time and turning it all into smoking rubble.</p>
<p>Briefly I tried to return a channel instead of an array of entries. I tried to be smart and find some use of Go channels in my program. Though it was a breeze to write, it was not really fitting into my usage patterns. So I decided to use simple arrays and worry about looking pro and hip later.</p>
<p>To display the entries I shopped around for a library again. And this, I think, is a really strong point of using Go. There&rsquo;s a library for anything. Even though C++ has been around for a lot longer, it&rsquo;s often impossible or really difficult to find libraries that do half of what Go libraries do. Writing a tool like this in C++ would be difficult. Anyhow, the library I found is called <a href="https://github.com/olekukonko/tablewriter">tablewriter</a>. It&rsquo;s pretty flexible and draws nice looking tables.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ klk display --grep <span class="s1">&#39;code|test&#39;</span>
</span></span><span class="line"><span class="cl">+----+-------------+----------+----------+-----------------------------+-------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> ID <span class="p">|</span>     DAY     <span class="p">|</span>   TIME   <span class="p">|</span> DURATION <span class="p">|</span>           COMMENT           <span class="p">|</span>    TAGS     <span class="p">|</span>
</span></span><span class="line"><span class="cl">+----+-------------+----------+----------+-----------------------------+-------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span>  <span class="m">2</span> <span class="p">|</span> <span class="m">30</span> Jan <span class="m">2019</span> <span class="p">|</span> 21:34:17 <span class="p">|</span>    15:14 <span class="p">|</span> Writing tests               <span class="p">|</span> <span class="c1">#test       |</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>  <span class="m">3</span> <span class="p">|</span>             <span class="p">|</span> 21:55:04 <span class="p">|</span>    20:08 <span class="p">|</span> Writing code                <span class="p">|</span> <span class="c1">#code       |</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>  <span class="m">4</span> <span class="p">|</span>             <span class="p">|</span> 22:20:38 <span class="p">|</span>    25:05 <span class="p">|</span> Writing more code and tests <span class="p">|</span> <span class="c1">#code #test |</span>
</span></span><span class="line"><span class="cl">+----+-------------+----------+----------+-----------------------------+-------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span>                     TOTAL   <span class="p">|</span> 1:00:27  <span class="p">|</span>
</span></span><span class="line"><span class="cl">+----+-------------+----------+----------+-----------------------------+-------------+
</span></span></code></pre></div><p>I wasted a huge amount of time trying to make the report look nicer. In the process I figured out I&rsquo;m not a good UI designer, even if it&rsquo;s just a basic text UI. I think I&rsquo;ll just copy what <code>timetrap</code> prints. For now this will do.</p>
<p>Now my application is almost 500 lines of code. That doesn&rsquo;t sound much, but it could already do the most basic time tracking. I&rsquo;d say 90% of what I use in <code>timetrap</code>. Next step would be to worry about <strong>U</strong> and <strong>D</strong>. And more sophisticated tag filtering, the whole reason I started this.</p>
<p>One thing that worries me a little is the executable size. I&rsquo;m at 11 megabytes now and the app doesn&rsquo;t do very much. When I tried SQLite, it was 16 MB. Sounds a bit excessive. I&rsquo;ll investigate later, but I think the most of the bulk is coming from Cobra. I might consider getting rid of, since I don&rsquo;t use any of the advanced features anyway.</p>
<hr>
<p>Google searches that went into getting this to work:</p>
<ul>
<li>golang channel</li>
<li>golang make empty channel</li>
<li>golang close channel</li>
<li>golang read from channel</li>
<li>golang for range</li>
<li>golang for range channel with index</li>
<li>golang display table</li>
<li>golang read line</li>
<li>golang unmarshal string</li>
<li>golang tostring</li>
<li>golang create empty slice</li>
<li>golang regexp flags</li>
<li>golang int to string</li>
<li>golang golang cannot define new methods on non-local type</li>
<li>golang format duration</li>
<li>golang init primitive type</li>
<li>golang read text file as string</li>
<li>golang sizeof</li>
<li>golang sizeof int</li>
<li>golang log fatal</li>
<li>golang exit vs panic</li>
<li>golang recover</li>
<li>golang fallthrough case</li>
<li>golang atexit</li>
</ul>
<hr>
<ul>
<li>Time spent: 5:15 hours</li>
<li>Total time spent: 16:15 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance: backend</title><link>https://detunized.net/posts/2019-01-27-giving-go-another-chance-backend/</link><pubDate>Sun, 27 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-27-giving-go-another-chance-backend/</guid><description>This time I was ready to do some serious enterprise level work with abstract interfaces and factories. I wanted to finally store the log entries I could create with in and out commands. After some researching and screwing around with go-sqlite3 for a bit, I decided not to mess with the database at the moment. I&amp;rsquo;m not that enterprise ready yet. As much as I admire the engineering effort behind SQLite and the idea of everything-in-one-single-file storage, I don&amp;rsquo;t really want to have a binary blob anywhere in my ~.</description><content type="html"><![CDATA[<p>This time I was ready to do some serious enterprise level work with abstract interfaces and factories. I wanted to finally store the log entries I could create with <code>in</code> and <code>out</code> commands. After some researching and screwing around with <a href="https://github.com/mattn/go-sqlite3">go-sqlite3</a> for a bit, I decided not to mess with the database at the moment. I&rsquo;m not <em>that</em> enterprise ready yet. As much as I admire the engineering effort behind SQLite and the idea of <em>everything-in-one-single-file</em> storage, I don&rsquo;t really want to have a binary blob anywhere in my <code>~</code>. For now at least.</p>
<p>I though it would be a good start to store the entries in a simple text file, where each line is a JSON object. I started using this format a long time ago as I find it a much better alternative to a giant JSON array.</p>
<p>Consider JSON-per-line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nt">&#34;comment&#34;</span><span class="p">:</span> <span class="s2">&#34;writing tests&#34;</span><span class="p">,</span> <span class="nt">&#34;tags&#34;</span><span class="p">:[]}</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nt">&#34;comment&#34;</span><span class="p">:</span> <span class="s2">&#34;writing more tests&#34;</span><span class="p">,</span> <span class="nt">&#34;tags&#34;</span><span class="p">:[]}</span>
</span></span></code></pre></div><p>vs. regular JSON:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nt">&#34;comment&#34;</span><span class="p">:</span> <span class="s2">&#34;writing tests&#34;</span><span class="p">,</span> <span class="nt">&#34;tags&#34;</span><span class="p">:[]},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nt">&#34;comment&#34;</span><span class="p">:</span> <span class="s2">&#34;writing more tests&#34;</span><span class="p">,</span> <span class="nt">&#34;tags&#34;</span><span class="p">:[]}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></div><p>Yes, it&rsquo;s possible to load the regular JSON file with one line of code and it&rsquo;s nice. But generating those files is painful. You&rsquo;d have to worry about surrounding <code>[]</code> and trailing commas on every line but the last one. Where the <em>JSON-per-line</em> format is much simpler. Just append a line with a JSON object and you&rsquo;re done. Usually parsing is faster too. Probably due to some non-linearities in JSON parsing or reallocation. Basically, it&rsquo;s faster to parse a million small JSON objects than a single JSON array with a million entries.</p>
<p>It would be totally unprofessional of me to write any functions and call them directly. I&rsquo;d need an interface first. After some googling and fighting with squiggly red and green lines in VS Code I was able to write my first interface in Go.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Backend ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Backend</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">OpenEntry</span><span class="p">(</span><span class="nx">comment</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">startTime</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">,</span> <span class="nx">tags</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">    <span class="nf">CloseEntry</span><span class="p">(</span><span class="nx">endTime</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">)</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Notice the <code>// Backend ...</code> comment. It&rsquo;s there for a reason. It makes the linter happy, as I don&rsquo;t want to have green squigglies everywhere. Apparently the linter is not happy about the exported type without a comment. The type is exported if it starts with a capital letter, by the way. Some interesting design decision.</p>
<p>In Go anything that implements these two functions is considered a backend. No explicit interface implementation is needed. Neat and scary. So the simplest form would be a null backend:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// NullBackend ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">NullBackend</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// OpenEntry ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">backend</span> <span class="o">*</span><span class="nx">NullBackend</span><span class="p">)</span> <span class="nf">OpenEntry</span><span class="p">(</span><span class="nx">comment</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">startTime</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">,</span> <span class="nx">tags</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CloseEntry ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">backend</span> <span class="o">*</span><span class="nx">NullBackend</span><span class="p">)</span> <span class="nf">CloseEntry</span><span class="p">(</span><span class="nx">endTime</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The next step was to write the actual file backing storage. That wasn&rsquo;t that difficult ether. I had to figure out how to serialize a struct into JSON. It&rsquo;s pretty easy in Go:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">encoded</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">entry</span><span class="p">)</span>
</span></span></code></pre></div><p>And then append it to a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">f</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">OpenFile</span><span class="p">(</span><span class="s">&#34;entries.json&#34;</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">O_RDWR</span><span class="p">|</span><span class="nx">os</span><span class="p">.</span><span class="nx">O_CREATE</span><span class="p">|</span><span class="nx">os</span><span class="p">.</span><span class="nx">O_APPEND</span><span class="p">,</span> <span class="mo">0644</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">f</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">encoded</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">f</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="s">&#34;\n&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>And our hand rolled NoSQL database is ready. Might even compete with MongoDB soon, just need to add a couple more features and a bunch of security holes. Ship it! 🚢</p>
<hr>
<p>Google searches that went into getting this to work:</p>
<ul>
<li>go sqlite</li>
<li>go check if file exists</li>
<li>go implement interface</li>
<li>go pointer receiver</li>
<li>golang pointer receiver interface</li>
<li>go interface</li>
<li>golang ISO8601 date</li>
<li>golang write file</li>
<li>golang serialize to json</li>
<li>golang marshal unexported fields</li>
<li>golang writeline</li>
</ul>
<hr>
<ul>
<li>Time spent: 3:50 hours</li>
<li>Total time spent: 11:00 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance: refactoring</title><link>https://detunized.net/posts/2019-01-26-giving-go-another-chance-refactoring/</link><pubDate>Sat, 26 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-26-giving-go-another-chance-refactoring/</guid><description>In this session I wanted to add another command. I imagined I&amp;rsquo;d have to factor out some common code and put it elsewhere. So I started by duplicating a file and trying to build it. And then I had another Go WTF moment. Apparently I cannot have a variable or function with the same name in different files. That I didn&amp;rsquo;t expect to run into. I guess it&amp;rsquo;s kinda like in C, only there that would be a link error.</description><content type="html"><![CDATA[<p>In this session I wanted to add another command. I imagined I&rsquo;d have to factor out some common code and put it elsewhere. So I started by duplicating a file and trying to build it. And then I had another Go WTF moment. Apparently I cannot have a variable or function with the same name in different files. That I didn&rsquo;t expect to run into. I guess it&rsquo;s kinda like in C, only there that would be a link error. But any intuition I had so far built up for this language just evaporated at this point.</p>
<p>I started googling around and found out that all the files in a package could be essentially treated as one single file. There&rsquo;s no such thing as a variable or a function local to a file. Can have many <code>init</code>s though. Anything starting with a capital letter is public and exported from the package (not file). Identifiers starting with a lowercase letter are private or hidden and are not exposed to the users of the package.</p>
<p>I normally refactor all the time. I move code around. I add, delete and re-add functions all the time. I rename everything constantly until I&rsquo;m happy with the result. Go in general doesn&rsquo;t prevent this style of developing, but it makes it a bit harder by not allowing to have any unused variables or imports. The imports are easily fixed the <code>goimports</code> or <code>goreturns</code> formatting tools. The local variables have to be removed by hand. This slows down the progress for me quite a bit. I&rsquo;d rather have the compiler show me a warning instead. I&rsquo;d get stuff to work first and then I&rsquo;d clean up the warning. Go doesn&rsquo;t forgive or forget.</p>
<p>As a result of this session I have all shared code extracted and tucked away in a separate file. I have two commands now: <code>in</code> and <code>out</code>. Things are cleaned up and are ready to be made into a great app.</p>
<p>Next time I start coding something more serious. Enough with the baby stuff, I&rsquo;m a senior developer after all.</p>
<hr>
<p>Google searches that went into getting this to work:</p>
<ul>
<li>go private function</li>
<li>go function local to a file</li>
</ul>
<hr>
<ul>
<li>Time spent: 35 minutes</li>
<li>Total time spent: 7:10 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance: hashtag parsing</title><link>https://detunized.net/posts/2019-01-25-giving-go-another-chance-hashtag-parsing/</link><pubDate>Fri, 25 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-25-giving-go-another-chance-hashtag-parsing/</guid><description>Last time I added the --tag/-t flag. This time I&amp;rsquo;d like to add hashtag parsing. I&amp;rsquo;d like to simply mention tags in the comment and have to tool fish them out for me. For example:
$ klk in &amp;#39;Writing some #tests to find a #bug&amp;#39; My first impulse is to look for a library. And there is one for exactly this purpose. But who wants to introduce a left-pad timebomb into their own codebase?</description><content type="html"><![CDATA[<p>Last time I added the <code>--tag/-t</code> flag. This time I&rsquo;d like to add hashtag parsing. I&rsquo;d like to simply mention tags in the comment and have to tool fish them out for me. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ klk in <span class="s1">&#39;Writing some #tests to find a #bug&#39;</span>
</span></span></code></pre></div><p>My first impulse is to look for a library. And there is <a href="https://github.com/gernest/mention">one</a> for exactly this purpose. But who wants to introduce a <a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/">left-pad timebomb</a> into their own codebase? <em>(Answer: almost anybody).</em> I&rsquo;d rather reinvent the wheel and <a href="https://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/">solve the problem with a regex</a>. Two problems are better than one.</p>
<p>Go has the <code>regexp</code> package in its standard library just for this purpose. So it&rsquo;s really a two-liner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">extractTags</span><span class="p">(</span><span class="nx">text</span> <span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">re</span> <span class="o">:=</span> <span class="nx">regexp</span><span class="p">.</span><span class="nf">MustCompile</span><span class="p">(</span><span class="s">&#34;#\\S+&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// TODO: Strip out #
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nx">re</span><span class="p">.</span><span class="nf">FindAllString</span><span class="p">(</span><span class="nx">text</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Notice a TODO? That one of those cases where you&rsquo;d have to take a dive from Python heights into C depths and roll you own loop. The two-liner becomes a six-liner. This where I start missing Ruby, Python, C#, Scala, Kotlin, hell, even C++.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">extractTags</span><span class="p">(</span><span class="nx">comment</span> <span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">re</span> <span class="o">:=</span> <span class="nx">regexp</span><span class="p">.</span><span class="nf">MustCompile</span><span class="p">(</span><span class="s">&#34;#\\S+&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tags</span> <span class="o">:=</span> <span class="nx">re</span><span class="p">.</span><span class="nf">FindAllString</span><span class="p">(</span><span class="nx">comment</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Strip #s
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">tag</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tags</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tags</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">TrimLeft</span><span class="p">(</span><span class="nx">tag</span><span class="p">,</span> <span class="s">&#34;#&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">tags</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Done with C? Back to Python. Now we have to join tags that come from the <code>--tag</code> switch with the ones we fished out from the comment. That is surprisingly easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">tags</span> <span class="o">:=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">tagFlag</span><span class="p">,</span> <span class="nf">extractTags</span><span class="p">(</span><span class="nx">comment</span><span class="p">)</span><span class="o">...</span><span class="p">)</span>
</span></span></code></pre></div><p>As was promised earlier, we have another problem now. Tags duplicate when the same tag comes from different places or when the same tag mentioned more than once. That should be quick to fix with in a language with such an amazing runtime, right? No, not really. One of the solutions would be to create a map and put all the tags in it, then iterate over its keys and put them into an array.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">tagSet</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">bool</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tag</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tags</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tagSet</span><span class="p">[</span><span class="nx">tag</span><span class="p">]</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">uniqueTags</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">tag</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tagSet</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">uniqueTags</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">uniqueTags</span><span class="p">,</span> <span class="nx">tag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Compare to Ruby:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">uniqueTags</span> <span class="o">=</span> <span class="n">tags</span><span class="o">.</span><span class="n">uniq</span>
</span></span></code></pre></div><p>Anywho, the final code looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">getTags</span><span class="p">(</span><span class="nx">comment</span> <span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tags</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">bool</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Add all flags from --tag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tag</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tagFlag</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tags</span><span class="p">[</span><span class="nx">tag</span><span class="p">]</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Add all flags from #tag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tag</span> <span class="o">:=</span> <span class="k">range</span> <span class="nf">extractTags</span><span class="p">(</span><span class="nx">comment</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tags</span><span class="p">[</span><span class="nx">tag</span><span class="p">]</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Deduplicate
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">uniqueTags</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">tag</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tags</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">uniqueTags</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">uniqueTags</span><span class="p">,</span> <span class="nx">tag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">uniqueTags</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>After all this hard work I can write this in the terminal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ klk in --at <span class="s1">&#39;15 min ago&#39;</span> --tag coding <span class="s2">&#34;Writing some #tests&#34;</span>
</span></span><span class="line"><span class="cl">Adding an entry: Writing some <span class="c1">#tests at Fri Jan 25 01:53:05 CET 2019 with tags #coding #tests</span>
</span></span></code></pre></div><p>The dream time tracking tool is coming along. And I&rsquo;m relearning myself some C loops. Double win!</p>
<hr>
<p>Google searches that went into getting this to work:</p>
<ul>
<li>golang tag parser</li>
<li>golang hashtag parser</li>
<li>go regex</li>
<li>golang map</li>
<li>golang unique slice of strings</li>
<li>golang merge slices unique</li>
<li>golang merge slices</li>
<li>golang set</li>
<li>golang map keys as slice</li>
<li>golang map get keys as slice</li>
<li>go for</li>
<li>go foreach</li>
<li>go for range modify</li>
<li>go for range update</li>
</ul>
<hr>
<ul>
<li>Time spent: 1 hour</li>
<li>Total time spent: 6:35 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance: more ambitious parsing</title><link>https://detunized.net/posts/2019-01-23-giving-go-another-chance-more-ambitious-parsing/</link><pubDate>Wed, 23 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-23-giving-go-another-chance-more-ambitious-parsing/</guid><description>Last time I was able to add some simple command line switches and it was easy. Now let&amp;rsquo;s try something harder. First of all I&amp;rsquo;d like to be able to override the current time with a user specified one. I&amp;rsquo;d like to able to log a task at a specific time. Sometimes I&amp;rsquo;m so itching to get something done, that I forget about all those good habits I&amp;rsquo;m trying to acquire and start hacking right away.</description><content type="html"><![CDATA[<p>Last time I was able to add some simple command line switches and it was easy. Now let&rsquo;s try something harder. First of all I&rsquo;d like to be able to override the current time with a user specified one. I&rsquo;d like to able to log a task at a specific time. Sometimes I&rsquo;m so itching to get something done, that I forget about all those good habits I&rsquo;m trying to acquire and start hacking right away. Later I realize that I forgot to log a task I&rsquo;m working on. With <a href="https://github.com/samg/timetrap">timetrap</a> it&rsquo;s quite easy to start a task 15 minutes ago instead of now:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ timetrap in --at <span class="s1">&#39;15 min ago&#39;</span> <span class="s1">&#39;Implementing --at parsing&#39;</span>
</span></span></code></pre></div><p>I&rsquo;d like to be able to do exactly the same. I looked around and found a library with ungogleable name <a href="https://github.com/olebedev/when">when</a>. Actually, Go also is tough to google. I wonder if Google had to hack their algorithms to promote their language when people look for Go.</p>
<p>With some copying from the README and minor googling I was able to get it done in under 10 minutes. Not bad at all.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;github.com/olebedev/when&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;github.com/olebedev/when/rules/common&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;github.com/olebedev/when/rules/en&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">parseAt</span><span class="p">(</span><span class="nx">at</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">now</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">at</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">now</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">w</span> <span class="o">:=</span> <span class="nx">when</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">w</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">en</span><span class="p">.</span><span class="nx">All</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">w</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">common</span><span class="p">.</span><span class="nx">All</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">parsed</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">w</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">at</span><span class="p">,</span> <span class="nx">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="o">||</span> <span class="nx">parsed</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Don&#39;t know how to parse &#39;%s&#39;\n&#34;</span><span class="p">,</span> <span class="nx">at</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">parsed</span><span class="p">.</span><span class="nx">Time</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Next I wanted to add some tags with <code>--tag/-t</code>. The tricky thing is, I want to be able to repeat this flag many times, if I wanted to.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ klk in -t coding -t tests <span class="s2">&#34;Writing some tests&#34;</span>
</span></span></code></pre></div><p>It turned out to be quite easy with Cobra. I didn&rsquo;t have to write much code for that. I compensated with a lot of googling, though.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Declare a variable
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">tags</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Add a flag
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">inCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">StringSliceVarP</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">tags</span><span class="p">,</span> <span class="s">&#34;tag&#34;</span><span class="p">,</span> <span class="s">&#34;t&#34;</span><span class="p">,</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{},</span> <span class="s">&#34;tags to use with this entry&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>I could even do <code>-t 'tag1,tag2'</code> instead of <code>-t tag1 -t tag2</code> now. It&rsquo;s important to save keystrokes. No one wants to type when they don&rsquo;t have to, and I don&rsquo;t want people to abandon my software when they see how much they need to type.</p>
<p>This thing is really taking shape now. It&rsquo;s got custom timestamp and tags. It&rsquo;s already much better than Microsoft Project.</p>
<hr>
<p>Google searches that went into getting this to work:</p>
<ul>
<li>go natural language date</li>
<li>go check string empty</li>
<li>go cobra repeated flag <em>(led to a lot of reading on Github)</em></li>
<li>golang strings</li>
</ul>
<hr>
<ul>
<li>Time spent: 20 minutes</li>
<li>Total time spent: 5:35 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance: easy parameter parsing</title><link>https://detunized.net/posts/2019-01-22-giving-go-another-chance-easy-parameter-parsing/</link><pubDate>Tue, 22 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-22-giving-go-another-chance-easy-parameter-parsing/</guid><description>It&amp;rsquo;s good to start with something easy. I already have a command klk in, now it would be good to pass some switches and arguments to it. Cobra has a huge readme, but I found that it was easier to check their own command line tool source for examples.
var inCmd = &amp;amp;cobra.Command{ Use: &amp;#34;in [comment]&amp;#34;, Aliases: []string{&amp;#34;i&amp;#34;}, Short: &amp;#34;Clock in an entry&amp;#34;, Run: func(cmd *cobra.Command, args []string) { comment := strings.</description><content type="html"><![CDATA[<p>It&rsquo;s good to start with something easy. I already have a command <code>klk in</code>, now it would be good to pass some switches and arguments to it. Cobra has a huge <a href="https://github.com/spf13/cobra/blob/master/README.md">readme</a>, but I found that it was easier to check their own command line tool source for examples.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">inCmd</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Use</span><span class="p">:</span>     <span class="s">&#34;in [comment]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Aliases</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;i&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Short</span><span class="p">:</span>   <span class="s">&#34;Clock in an entry&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Run</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">comment</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">args</span><span class="p">,</span> <span class="s">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nx">timestamp</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Adding an entry:&#34;</span><span class="p">,</span> <span class="nx">comment</span><span class="p">,</span> <span class="s">&#34;at&#34;</span><span class="p">,</span> <span class="nx">timestamp</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">UnixDate</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I had to figure out how to join strings and how to custom format a date. The default date format is somewhat weird. I&rsquo;m guessing its following the Go path (GOPATH?) on it&rsquo;s journey into the weird. Who wants the date printed like this by default? What&rsquo;s <code>m=+0.001801899</code>?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">2019-01-23 12:04:12.760058 +0100 CET <span class="nv">m</span><span class="o">=</span>+0.001801899
</span></span></code></pre></div><p>Outside of this it was quite easy. Overall Go feels like C with some Python sprinkled on top of it. The problem is, you never know when you have to drop from Python to C and try not to bang your head in the process.</p>
<p>Now let&rsquo;s add a switch. The easiest I could think of is <code>--quiet/-q</code>. It&rsquo;s just one line with Cobra:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">inCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">BoolVarP</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">quiet</span><span class="p">,</span> <span class="s">&#34;quiet&#34;</span><span class="p">,</span> <span class="s">&#34;q&#34;</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="s">&#34;be quiet and don&#39;t print so much stuff&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>The final product works like this now:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ klk in some work
</span></span><span class="line"><span class="cl">Adding an entry: some work at Wed Jan <span class="m">23</span> 12:16:20 CET <span class="m">2019</span>
</span></span></code></pre></div><p>or</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ klk in -q some work
</span></span></code></pre></div><p>Ship it! 🚢</p>
<hr>
<p>Google searches that went into getting this to work:</p>
<ul>
<li>go join strings</li>
<li>go time now</li>
<li>go format date</li>
</ul>
<hr>
<ul>
<li>Time spent: 15 minutes</li>
<li>Total time spent: 5:15 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance: command line arguments</title><link>https://detunized.net/posts/2019-01-21-giving-go-another-chance-command-line-arguments/</link><pubDate>Mon, 21 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-21-giving-go-another-chance-command-line-arguments/</guid><description>To make a usable command line tool one has to parse the arguments first. I shopped around for a command line parsing library and found cli and cobra. I tried cli briefly and then switched to cobra. You gotta go with the stars, Github stars. Cobra&amp;rsquo;s got more of them.
Cobra comes with a cogeden tool which is nice, I guess. Since I&amp;rsquo;m still shaky with even basic Go concepts, I decided to run and not type up everything from scratch.</description><content type="html"><![CDATA[<p>To make a usable command line tool one has to parse the arguments first. I shopped around for a command line parsing library and found <a href="https://github.com/urfave/cli">cli</a> and <a href="https://github.com/spf13/cobra">cobra</a>. I tried cli briefly and then switched to cobra. You gotta go with the stars, Github stars. Cobra&rsquo;s got more of them.</p>
<p>Cobra comes with a cogeden tool which is nice, I guess. Since I&rsquo;m still shaky with even basic Go concepts, I decided to run and not type up everything from scratch.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ~/go/bin/cobra init klk
</span></span><span class="line"><span class="cl">Your Cobra application is ready at
</span></span><span class="line"><span class="cl">/Users/detunized/go/src/klk
</span></span></code></pre></div><p>Why? I ran it in a different folder. And then I have a flashback and I realize why I had that bitter taste in my mouth the last time I used go. It&rsquo;s the <code>$GOPATH</code> thing. No other language I know does anything like it. Apparently I cannot have my code where I want it. It has to be where Rob Pike wants it. Okay, I thought to myself, I can just copy the generated files to where I want them and I&rsquo;m done. And I did.</p>
<p>Cobra provides a way to add a new command with the codegen tool like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ~/go/bin/cobra add in
</span></span><span class="line"><span class="cl">in created at /Users/detunized/go/src/klk/cmd/in.go
</span></span></code></pre></div><p>This adds the subcommand called <code>in</code>. To be used like this: <code>klk in</code>. So far so good. I copied the newly created file to my repo and kept poking around, editing, running, see what my changes do. And then I had a first WTF moment. I change some files, but Go doesn&rsquo;t pick up the changes and it seems like it runs the old executable. I spent lots of time trying to figure out why the build silently fails and I don&rsquo;t see any error messages. And then it hit me: it keeps building some of the files from the <code>$GOPATH</code> and some from my project folder. So copying files from the default place to an external folder is not really an option.</p>
<p>Mmkay. But I still wanted my files in my project folder. Let&rsquo;s see if symlinking my folder into <code>$GOPATH/src</code> would trick the compiler:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ln -s <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span> /Users/detunized/go/src/klk
</span></span></code></pre></div><p>So far it&rsquo;s working and I&rsquo;m able to use <code>cobra</code> tool and the compiler is happy. But let&rsquo;s finally get to coding next time. This time it didn&rsquo;t happen.</p>
<hr>
<p>Google searches that went into getting this to work:</p>
<ul>
<li>go cli</li>
<li>go cli parser</li>
<li>go cli vs cobra</li>
<li>cobra Error: Rel: can&rsquo;t make relative to</li>
<li>gopath</li>
<li>gopath vs goroot</li>
<li>go package init</li>
<li>go build without gopath</li>
<li>no gopath</li>
<li>vgo</li>
</ul>
<hr>
<ul>
<li>Time spent: 2 hours</li>
<li>Total time spent: 5 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance: setting everything up</title><link>https://detunized.net/posts/2019-01-20-giving-go-another-chance-setting-everything-up/</link><pubDate>Sun, 20 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-20-giving-go-another-chance-setting-everything-up/</guid><description>First things first: I need to be able to build and run Go programs. I&amp;rsquo;m using macOS, so installing Go was very quick:
$ brew install go Seems to work:
$ go version go version go1.11.4 darwin/amd64 Now I need to hook it up with my editor. I&amp;rsquo;d like to have at least some basic features, like format code on save and build and run from the editor. Normally I use Sublime Text 3, but after quickly checking the installation instructions I decided to rather try Visual Studio Code where I can set everything up with one click.</description><content type="html"><![CDATA[<p>First things first: I need to be able to build and run Go programs. I&rsquo;m using
macOS, so installing Go was very quick:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ brew install go
</span></span></code></pre></div><p>Seems to work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ go version
</span></span><span class="line"><span class="cl">go version go1.11.4 darwin/amd64
</span></span></code></pre></div><p>Now I need to hook it up with my editor. I&rsquo;d like to have at least some basic features, like format code on save and build and run from the editor. Normally I use Sublime Text 3, but after quickly checking the <a href="https://margo.sh/b/hello-margo/">installation instructions</a> I decided to rather try Visual Studio Code where I can set everything up with one click.</p>
<p>It&rsquo;s been laying around on my hard drive for a while now and now I finally have an excuse to try it out. So, not only a new language, a new editor as well. Yay!</p>
<p>Setting up the extension took just a couple of minutes, it was quick and absolutely painless. Making the editor format the code on save, on the other hand, was a whole different story. It took me a really long time fiddling with the settings, googling, reading <a href="https://github.com/Microsoft/vscode-go/issues/1419">relevant issues</a> on Github and so on. VS Code was very helpful most of the time, installing relevant tools, highlighting errors in my config and so forth. The only thing it refused to do is to format my code on save.</p>
<p>It seems I&rsquo;ve tried everything and I just gave up and went to bed. As it turned out next morning, all it needed is a restart. How dumb of me. I guess it&rsquo;s that my never dying faith in software and that developers for once will get it right. No such luck this time. At least Windows 95 was honest about this. VS Code makes it look like no restart is needed and everything is kinda working but not 100%. More like 93.76%.</p>
<p>In the end I have it all working: format on save, build, go to definition and even full blown IntelliSense, that autocompletes almost everything for me. Nice!</p>
<p>Let&rsquo;s get to coding next time.</p>
<hr>
<p>This is how much I rely on Google these days to get this little bit of work done:</p>
<ul>
<li>go tutorial</li>
<li>homebrew install golang</li>
<li>goimports</li>
<li>goreturns</li>
<li>goformat</li>
<li>vscode go fmt on save</li>
<li>vscode go format on save not working</li>
<li>go vet</li>
<li>go build release</li>
</ul>
<hr>
<ul>
<li>Time spent: 3 hours</li>
<li>Total time spent: 3 hours</li>
</ul>
]]></content></item><item><title>Giving Go another chance</title><link>https://detunized.net/posts/2019-01-19-giving-go-another-chance/</link><pubDate>Sat, 19 Jan 2019 00:00:00 +0000</pubDate><guid>https://detunized.net/posts/2019-01-19-giving-go-another-chance/</guid><description>I decided to start tracking the time I spent on tasks a bit better than with a pen and a piece of paper. After a bit of searching I came across this list. I tried all of the tools on it and I stuck with timetrap for a while. But some things bugged me about it.
First of all I&amp;rsquo;d like to be able to add tags without additional parsing or relying on grep.</description><content type="html"><![CDATA[<p>I decided to start tracking the time I spent on tasks a bit better than with a pen and a piece of paper. After a bit of searching I came across this <a href="https://www.linuxlinks.com/timetrackers/">list</a>. I tried all of the tools on it and I stuck with <a href="https://www.linuxlinks.com/timetrap/">timetrap</a> for a while. But some things bugged me about it.</p>
<p>First of all I&rsquo;d like to be able to add tags without additional parsing or relying on <code>grep</code>. I also would like to be able to backup the database to something centralized or even better use a git repo as a backend. And most of all I wanted to scratch an itch and write a simple tool in a new (sort of) language.</p>
<p>I started to poke around Rust a little while ago and I thought it would be a great fit. But after looking for crates (libraries) that I would need, I decided to give Go another chance. I used it before at work at some point and it kinda worked and was a good fit there. I also remember to have a bitter taste in my mouth after using it back then. So I&rsquo;m gonna try it again and see how it feels a few years later.</p>
<p>I&rsquo;m gonna document my steps and track the time I spent on the tasks. Let&rsquo;s start!</p>
]]></content></item></channel></rss>