<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>thacoon's Blog</title><link>https://thacoon.com/</link><description>Recent content on thacoon's Blog</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><managingEditor>thacoon@posteo.de (thacoon)</managingEditor><webMaster>thacoon@posteo.de (thacoon)</webMaster><copyright>thacoon</copyright><lastBuildDate>Sun, 04 May 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://thacoon.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Compute Shaders with Shader Storage Buffer Objects (SSBO) in OpenGL with Rust</title><link>https://thacoon.com/blog/compute-shaders-ssbo-opengl/</link><pubDate>Sun, 04 May 2025 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/compute-shaders-ssbo-opengl/</guid><description>Introduction I am interested in on how to compute stuff on my GPU, for a hobby project I am currently working it. Previously, I learned a bit about OpenGL, while writing my own (bad) renderer for Gaussian Splatting, so I wanted to learn how to do it with OpenGL.
So, I found this great tutorial about compute shaders in opengl 4.3 by dan coady. It explains how to use compute shaders in OpenGL, but instead of shader storage buffer objects (SSBOs) textures are used.</description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>I am interested in on how to compute stuff on my GPU, for a hobby project I am currently working it. Previously, I learned a bit about OpenGL, while writing my <a href="https://codeberg.org/thacoon/gaussian-splatting">own (bad) renderer for Gaussian Splatting</a>, so I wanted to learn how to do it with OpenGL.</p>
<p>So, I found this great tutorial about <a href="https://www.pondo.dev/blog/opengl_compute">compute shaders in opengl 4.3</a> by dan coady. It explains how to use compute shaders in OpenGL, but instead of shader storage buffer objects (SSBOs) textures are used. That works nicely but one drawback is that textures are more limited in their size than a SSBO. For example on my machine a SSBO can be up to 1GB big, while for a texture I can only have up to 64MB (16384 pixels with RGBA = 16384 * 4 bytes).</p>
<p>As I am missing the endurance to write a detailed in-depht blog article (I&rsquo;m sorry), I&rsquo;ll let the code below speak for itself. I have divided it into 9, hopefully self explaining, parts, that describe how to use compute shaders and shader storage buffer objects in OpenGL to do calculations on your GPU instead of your CPU.</p>
<h2 id="code">Code</h2>
<p>You can find the complete source code <a href="https://codeberg.org/thacoon/compute-shaders-ssbo-example">here</a>.</p>
<h3 id="part-1-set-up-opengl">Part 1: Set up OpenGL</h3>
<p>We need a context for OpenGL, this is mandatory. Normally we set up a window, but in our case we actually don&rsquo;t want to render/display anything, so we directly hide the window. That works for when running the code on your computer, but requires a display server like X11 or Wayland. That may be not the case if you want to execute this on a server. However, that should also be possible if you use <a href="https://www.khronos.org/egl/">EGL</a> instead of glfw to write a headless version, which can run even if no graphical interface exists.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// Set up context for OpenGL
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">glfw</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">glfw</span>::<span class="n">init</span><span class="p">(</span><span class="n">glfw</span>::<span class="n">fail_on_errors</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="n">glfw</span><span class="p">.</span><span class="n">window_hint</span><span class="p">(</span><span class="n">glfw</span>::<span class="n">WindowHint</span>::<span class="n">ContextVersionMajor</span><span class="p">(</span><span class="mi">4</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="n">glfw</span><span class="p">.</span><span class="n">window_hint</span><span class="p">(</span><span class="n">glfw</span>::<span class="n">WindowHint</span>::<span class="n">ContextVersionMinor</span><span class="p">(</span><span class="mi">6</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="n">glfw</span><span class="p">.</span><span class="n">window_hint</span><span class="p">(</span><span class="n">glfw</span>::<span class="n">WindowHint</span>::<span class="n">OpenGlProfile</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">        </span><span class="n">glfw</span>::<span class="n">OpenGlProfileHint</span>::<span class="n">Core</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="n">glfw</span><span class="p">.</span><span class="n">window_hint</span><span class="p">(</span><span class="n">glfw</span>::<span class="n">WindowHint</span>::<span class="n">FocusOnShow</span><span class="p">(</span><span class="kc">true</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="n">glfw</span><span class="p">.</span><span class="n">window_hint</span><span class="p">(</span><span class="n">glfw</span>::<span class="n">WindowHint</span>::<span class="n">Visible</span><span class="p">(</span><span class="kc">false</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="k">mut</span><span class="w"> </span><span class="n">window</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">glfw</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">create_window</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span><span class="w"> </span><span class="mi">256</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Compute shaders SSBO Example&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">glfw</span>::<span class="n">WindowMode</span>::<span class="n">Windowed</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Failed to create GLFW window.&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="c1">// Initialize OpenGL
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">gl</span>::<span class="n">load_with</span><span class="p">(</span><span class="o">|</span><span class="n">symbol</span><span class="o">|</span><span class="w"> </span><span class="n">window</span><span class="p">.</span><span class="n">get_proc_address</span><span class="p">(</span><span class="n">symbol</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">_</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-2-compile-the-shader">Part 2: Compile the shader</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">shader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">compile_shader</span><span class="p">(</span><span class="n">Path</span>::<span class="n">new</span><span class="p">(</span><span class="s">&#34;src/compute_shader.glsl&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">program</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">link_shader</span><span class="p">(</span><span class="n">shader</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">compile_shader</span><span class="p">(</span><span class="n">path</span>: <span class="kp">&amp;</span><span class="nc">Path</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">shader</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">code</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fs</span>::<span class="n">read_to_string</span><span class="p">(</span><span class="n">path</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;could not read shader file&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">code</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CString</span>::<span class="n">new</span><span class="p">(</span><span class="n">code</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="n">shader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gl</span>::<span class="n">CreateShader</span><span class="p">(</span><span class="n">gl</span>::<span class="no">COMPUTE_SHADER</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">ShaderSource</span><span class="p">(</span><span class="n">shader</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">code</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">(),</span><span class="w"> </span><span class="n">ptr</span>::<span class="n">null</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">CompileShader</span><span class="p">(</span><span class="n">shader</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="c1">// [...] omit error handling for simplicity
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="n">shader</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">link_shader</span><span class="p">(</span><span class="n">shader</span>: <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">program</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gl</span>::<span class="n">CreateProgram</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">AttachShader</span><span class="p">(</span><span class="n">program</span><span class="p">,</span><span class="w"> </span><span class="n">shader</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">LinkProgram</span><span class="p">(</span><span class="n">program</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">success</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gl</span>::<span class="no">FALSE</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">gl</span>::<span class="n">types</span>::<span class="n">GLint</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">GetProgramiv</span><span class="p">(</span><span class="n">program</span><span class="p">,</span><span class="w"> </span><span class="n">gl</span>::<span class="no">LINK_STATUS</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">success</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">        </span><span class="c1">// [...] omit error handling for simplicity
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">DeleteShader</span><span class="p">(</span><span class="n">shader</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">        </span><span class="n">program</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-3-our-compute-shader">Part 3: Our compute shader</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#version 460 core</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">local_size_x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">local_size_y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">local_size_z</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">in</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">std430</span><span class="p">,</span> <span class="n">binding</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="n">readonly</span> <span class="n">buffer</span> <span class="n">input_0_buffer</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">uint</span> <span class="n">input_0</span><span class="p">[];</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">std430</span><span class="p">,</span> <span class="n">binding</span> <span class="o">=</span> <span class="mi">2</span><span class="p">)</span> <span class="n">readonly</span> <span class="n">buffer</span> <span class="n">input_1_buffer</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">uint</span> <span class="n">input_1</span><span class="p">[];</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">layout</span><span class="p">(</span><span class="n">std430</span><span class="p">,</span> <span class="n">binding</span> <span class="o">=</span> <span class="mi">3</span><span class="p">)</span> <span class="n">writeonly</span> <span class="n">buffer</span> <span class="n">output_buffer</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">uint</span> <span class="n">data</span><span class="p">[];</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">uniform</span> <span class="n">uint</span> <span class="n">factor</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">uint</span> <span class="n">index</span> <span class="o">=</span> <span class="n">gl_GlobalInvocationID</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">data</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">input_0</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">*</span> <span class="n">input_1</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">*</span> <span class="n">factor</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="part-4-set-up-the-shader-storage-buffer-objects">Part 4: Set up the shader storage buffer objects</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c1">// Set up our shader storage buffer objects
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">input_1_ssbo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">setup_ssbo</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">size_of</span>::<span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="p">(),</span><span class="w"> </span><span class="n">gl</span>::<span class="no">DYNAMIC_DRAW</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">input_2_ssbo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">setup_ssbo</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">size_of</span>::<span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="p">(),</span><span class="w"> </span><span class="n">gl</span>::<span class="no">DYNAMIC_DRAW</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">output_ssbo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">setup_ssbo</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">size_of</span>::<span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="p">(),</span><span class="w"> </span><span class="n">gl</span>::<span class="no">STREAM_COPY</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">setup_ssbo</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="n">index</span>: <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="n">size</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="n">usage</span>: <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLenum</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">ssbo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">GenBuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">ssbo</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BindBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="n">ssbo</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BufferData</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">            </span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">            </span><span class="n">size</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">gl</span>::<span class="n">types</span>::<span class="n">GLsizeiptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">            </span><span class="n">ptr</span>::<span class="n">null</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">            </span><span class="n">usage</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BindBufferBase</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">,</span><span class="w"> </span><span class="n">ssbo</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BindBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">    </span><span class="n">ssbo</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-5-write-data-to-the-gpus-shader-storage-buffer-objects">Part 5: Write data to the GPU&rsquo;s shader storage buffer objects</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c1">// Write data to the GPU
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">input_1_data</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span><span class="w"> </span><span class="mi">10</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="n">update_ssbo_data</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">input_1_data</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">input_2_data</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">41</span><span class="p">,</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span><span class="w"> </span><span class="mi">43</span><span class="p">,</span><span class="w"> </span><span class="mi">44</span><span class="p">,</span><span class="w"> </span><span class="mi">45</span><span class="p">,</span><span class="w"> </span><span class="mi">46</span><span class="p">,</span><span class="w"> </span><span class="mi">47</span><span class="p">,</span><span class="w"> </span><span class="mi">48</span><span class="p">,</span><span class="w"> </span><span class="mi">49</span><span class="p">,</span><span class="w"> </span><span class="mi">50</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="n">update_ssbo_data</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">input_2_data</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">update_ssbo_data</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="n">index</span>: <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="n">data</span>: <span class="kp">&amp;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="n">offset</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">data_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">size_of</span>::<span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="p">()</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="n">len</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BindBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BufferSubData</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">            </span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">            </span><span class="n">offset</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">gl</span>::<span class="n">types</span>::<span class="n">GLsizeiptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">            </span><span class="n">data_size</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">gl</span>::<span class="n">types</span>::<span class="n">GLsizeiptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">            </span><span class="n">data</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">std</span>::<span class="n">ffi</span>::<span class="n">c_void</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BindBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-6-set-some-static-gpus-uniform-data">Part 6: Set some static GPU&rsquo;s uniform data</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c1">// Set some static data
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">factor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">4</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="n">set_uniform</span><span class="p">(</span><span class="s">&#34;factor&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">factor</span><span class="p">,</span><span class="w"> </span><span class="n">program</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">set_uniform</span><span class="p">(</span><span class="n">name</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">value</span>: <span class="kt">u32</span><span class="p">,</span><span class="w"> </span><span class="n">program</span>: <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">uniform_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CString</span>::<span class="n">new</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">UseProgram</span><span class="p">(</span><span class="n">program</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">uniform_location</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gl</span>::<span class="n">GetUniformLocation</span><span class="p">(</span><span class="n">program</span><span class="p">,</span><span class="w"> </span><span class="n">uniform_name</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">Uniform1ui</span><span class="p">(</span><span class="n">uniform_location</span><span class="p">,</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-7-compute-and-wait-for-the-results">Part 7: Compute and wait for the results</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c1">// Compute
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">UseProgram</span><span class="p">(</span><span class="n">program</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">DispatchCompute</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">            </span><span class="n">size</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">            </span><span class="mi">1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">            </span><span class="mi">1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">MemoryBarrier</span><span class="p">(</span><span class="n">gl</span>::<span class="no">ALL_BARRIER_BITS</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-8-read-back-data-from-the-gpus-shader-storage-buffer-objects-ssbo">Part 8: Read back data from the GPU&rsquo;s shader storage buffer objects (SSBO)</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c1">// Read back from the GPU
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">output_data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">read_ssbo_data</span><span class="p">(</span><span class="n">output_ssbo</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;[*] Output: </span><span class="si">{:?}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">output_data</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">read_ssbo_data</span><span class="p">(</span><span class="n">buffer</span>: <span class="nc">gl</span>::<span class="n">types</span>::<span class="n">GLuint</span><span class="p">,</span><span class="w"> </span><span class="n">size</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">data</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="n">size</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BindBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="n">buffer</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">data_ptr</span><span class="w"> </span><span class="o">=</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">            </span><span class="n">gl</span>::<span class="n">MapBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="n">gl</span>::<span class="no">READ_ONLY</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u32</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="n">data_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">            </span><span class="fm">panic!</span><span class="p">(</span><span class="s">&#34;Failed to map buffer&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="n">ptr</span>::<span class="n">copy_nonoverlapping</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">            </span><span class="n">data_ptr</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">            </span><span class="n">data</span><span class="p">.</span><span class="n">as_mut_ptr</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">            </span><span class="n">size</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="n">data</span><span class="p">.</span><span class="n">set_len</span><span class="p">(</span><span class="n">size</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">UnmapBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">BindBuffer</span><span class="p">(</span><span class="n">gl</span>::<span class="no">SHADER_STORAGE_BUFFER</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">    </span><span class="n">data</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-9-teardown-opengl">Part 9: Teardown OpenGL</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c1">// Teardown
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">DeleteProgram</span><span class="p">(</span><span class="n">program</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">DeleteShader</span><span class="p">(</span><span class="n">shader</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">DeleteBuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">input_1_ssbo</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">DeleteBuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">input_2_ssbo</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">        </span><span class="n">gl</span>::<span class="n">DeleteBuffers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">output_ssbo</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="c1">// [...]
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h3 id="part-10-running-the-code">Part 10: Running the Code</h3>





<pre tabindex="0"><code>$ cargo run
[*] Output: [164, 336, 516, 704, 900, 1104, 1316, 1536, 1764, 2000]</code></pre><h2 id="resources">Resources</h2>
<ul>
<li><a href="https://learnopengl.com/">Learn OpenGL</a></li>
<li><a href="https://github.com/bwasty/learn-opengl-rs">Rust port of Learn OpenGL</a></li>
<li><a href="https://www.pondo.dev/blog/opengl_compute">compute shaders in opengl 4.3</a></li>
<li><a href="https://www.khronos.org/opengl/wiki/Shader_Storage_Buffer_Object">Shader Storage Buffer Object</a></li>
<li><a href="https://www.uni-weimar.de/fileadmin/user/fak/medien/professuren/Computer_Graphics/CG_WS_18_19/CG/06_ShaderBuffers.pdf">Shader Buffers - Uniform &amp; Shader Storage Buffer Objects</a></li>
<li><a href="https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)">Data Types (GLSL)</a></li>
<li><a href="https://learnopengl.com/Guest-Articles/2022/Compute-Shaders/Introduction">Introduction - GPU Computing (Learn OpenGL)</a></li>
</ul>
]]></content:encoded></item><item><title>Prompt Battler is now Open-Source</title><link>https://thacoon.com/blog/prompt-battler/</link><pubDate>Wed, 19 Mar 2025 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/prompt-battler/</guid><description>Prompt Battler is now Open-Source From time to time I was working on an online-multiplayer web game, called Prompt Battler, which you can play here.
It is still in an early-development state, but you can find the code now here.</description><content:encoded><![CDATA[<h2 id="prompt-battler-is-now-open-source">Prompt Battler is now Open-Source</h2>
<p>From time to time I was working on an online-multiplayer web game, called Prompt Battler, which you can play <a href="https://prompt-battler.com">here</a>.</p>
<p>It is still in an early-development state, but you can find the code now <a href="https://codeberg.org/thacoon/prompt-battler">here</a>.</p>
<p><img alt="Screenshot of the landing page of Prompt Battler" src="/blog/prompt-battler/landing-page-2025-03-20.png"></p>
]]></content:encoded></item><item><title>Elixir and the Pin Operator</title><link>https://thacoon.com/blog/elixir-pin-operator/</link><pubDate>Sat, 18 Nov 2023 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/elixir-pin-operator/</guid><description>The Problem I am working on my first side project using Elixir and Phoenix. I was writing some tests and encountered a flaky unit test (randomly fails or passes). In this test, I wanted to make a case differentiation, but today I learned (TIL) in Elixir, the = behaves a bit differently. It is not an assignment operator; it is the match operator.
Pattern matching When you use =, Elixir tries to match the value on the right side of the = with the pattern on the left side.</description><content:encoded><![CDATA[<h2 id="the-problem">The Problem</h2>
<p>I am working on my first side project using Elixir and Phoenix. I was writing some tests and encountered a flaky unit test (randomly fails or passes). In this test, I wanted to make a case differentiation, but today I learned (TIL) in Elixir, the <code>=</code> behaves a bit differently. It is not an <em>assignment operator</em>; it is the <em>match operator</em>.</p>
<h3 id="pattern-matching">Pattern matching</h3>
<p>When you use <code>=</code>, Elixir tries to match the value on the right side of the <code>=</code> with the pattern on the left side. If the pattern on the left does not match the value on the right, an error is raised.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="mi">42</span>      <span class="c1"># Matches and binds the value 42 to the variable x</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">{</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">}</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">}</span>  <span class="c1"># Matches and binds 1 to a, and 2 to b</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="p">[</span><span class="n">head</span> <span class="o">|</span> <span class="n">tail</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>  <span class="c1"># Matches and binds head to 1, tail to [2, 3]</span></span></span></code></pre></div><p>The pin operator <code>^</code> is used to assert that a variable on the left side of the pattern should match the value on the right side. It prevents the introduction of a new variable with the same name.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="mi">42</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="o">^</span><span class="n">x</span> <span class="o">=</span> <span class="mi">42</span>  <span class="c1"># Matches because the value on the right matches the existing variable x</span></span></span></code></pre></div><p>Without the pin operator <code>^</code>, the second line would introduce a new variable named <code>x</code> rather than asserting that it should match the existing <code>x</code>.</p>
<h2 id="the-code">The Code</h2>
<p>In the given code snippet, we have a example test case that involves generating a new game response. The response includes information about the winner and loser of the game, identified by their respective user IDs.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">defmodule</span><span class="w"> </span><span class="n">ExampleTest</span><span class="w"> </span><span class="kr">do</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">  </span><span class="k">use</span><span class="w"> </span><span class="n">ExUnit</span><span class="p">.</span><span class="n">Case</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="n">def</span><span class="w"> </span><span class="n">get_new_game_response</span><span class="p">()</span><span class="w"> </span><span class="kr">do</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="n">random_number</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>:<span class="nc">rand</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="n">random_number</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="kr">do</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">      </span><span class="o">%</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="s">&#34;winner&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">%</span><span class="p">{</span><span class="s">&#34;user_id&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="s">&#34;foo&#34;</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">        </span><span class="s">&#34;looser&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">%</span><span class="p">{</span><span class="s">&#34;user_id&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="s">&#34;bar&#34;</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">      </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="k">else</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span><span class="o">%</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="s">&#34;winner&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">%</span><span class="p">{</span><span class="s">&#34;user_id&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="s">&#34;bar&#34;</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="s">&#34;looser&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">%</span><span class="p">{</span><span class="s">&#34;user_id&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="s">&#34;foo&#34;</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">      </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="n">end</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">  </span><span class="n">end</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">  </span><span class="n">test</span><span class="w"> </span><span class="s">&#34;new game response flacky&#34;</span><span class="w"> </span><span class="kr">do</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="n">player_1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;foo&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="n">player_2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;bar&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="n">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_new_game_response</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">    </span><span class="n">case</span><span class="w"> </span><span class="n">response</span><span class="p">[</span><span class="s">&#34;winner&#34;</span><span class="p">][</span><span class="s">&#34;user_id&#34;</span><span class="p">]</span><span class="w"> </span><span class="kr">do</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">      </span><span class="n">player_1</span><span class="w"> </span>-&gt;
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="nc">assert</span><span class="w"> </span><span class="n">response</span><span class="p">[</span><span class="s">&#34;looser&#34;</span><span class="p">][</span><span class="s">&#34;user_id&#34;</span><span class="p">]</span><span class="w"> </span><span class="o">=~</span><span class="w"> </span><span class="n">player_2</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">      </span><span class="n">player_2</span><span class="w"> </span>-&gt;
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="nc">assert</span><span class="w"> </span><span class="n">response</span><span class="p">[</span><span class="s">&#34;looser&#34;</span><span class="p">][</span><span class="s">&#34;user_id&#34;</span><span class="p">]</span><span class="w"> </span><span class="o">=~</span><span class="w"> </span><span class="n">player_1</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">      </span><span class="n">_</span><span class="w"> </span>-&gt;
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="nc">raise</span><span class="w"> </span><span class="s">&#34;Unexpected user_id&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">    </span><span class="n">end</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">  </span><span class="n">end</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">  </span><span class="n">test</span><span class="w"> </span><span class="s">&#34;new game response with pin operator&#34;</span><span class="w"> </span><span class="kr">do</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">    </span><span class="n">player_1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;foo&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">    </span><span class="n">player_2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;bar&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">    </span><span class="n">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_new_game_response</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">    </span><span class="n">case</span><span class="w"> </span><span class="n">response</span><span class="p">[</span><span class="s">&#34;winner&#34;</span><span class="p">][</span><span class="s">&#34;user_id&#34;</span><span class="p">]</span><span class="w"> </span><span class="kr">do</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">      </span><span class="o">^</span><span class="n">player_1</span><span class="w"> </span>-&gt;
</span></span><span class="line"><span class="ln">46</span><span class="cl">        <span class="nc">assert</span><span class="w"> </span><span class="n">response</span><span class="p">[</span><span class="s">&#34;looser&#34;</span><span class="p">][</span><span class="s">&#34;user_id&#34;</span><span class="p">]</span><span class="w"> </span><span class="o">=~</span><span class="w"> </span><span class="n">player_2</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">      </span><span class="o">^</span><span class="n">player_2</span><span class="w"> </span>-&gt;
</span></span><span class="line"><span class="ln">49</span><span class="cl">        <span class="nc">assert</span><span class="w"> </span><span class="n">response</span><span class="p">[</span><span class="s">&#34;looser&#34;</span><span class="p">][</span><span class="s">&#34;user_id&#34;</span><span class="p">]</span><span class="w"> </span><span class="o">=~</span><span class="w"> </span><span class="n">player_1</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">      </span><span class="n">_</span><span class="w"> </span>-&gt;
</span></span><span class="line"><span class="ln">52</span><span class="cl">        <span class="nc">raise</span><span class="w"> </span><span class="s">&#34;Unexpected user_id&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">    </span><span class="n">end</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">  </span><span class="n">end</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w"></span><span class="n">end</span></span></span></code></pre></div><h3 id="pattern-matching-without-pin-operator">Pattern Matching Without Pin Operator</h3>
<p>Let&rsquo;s look at the original version of the test case without using the pin operator:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">case</span> <span class="n">response</span><span class="p">[</span><span class="s2">&#34;winner&#34;</span><span class="p">][</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="n">player_1</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">assert</span> <span class="n">response</span><span class="p">[</span><span class="s2">&#34;looser&#34;</span><span class="p">][</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span> <span class="o">=~</span> <span class="n">player_2</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">player_2</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">assert</span> <span class="n">response</span><span class="p">[</span><span class="s2">&#34;looser&#34;</span><span class="p">][</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span> <span class="o">=~</span> <span class="n">player_1</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">raise</span> <span class="s2">&#34;Unexpected user_id&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">end</span></span></span></code></pre></div><p>In this version, we attempt to match the user IDs of the winners (player_1 and player_2) and take corresponding actions. However, this code has a subtle issue that might not be immediately apparent.</p>
<p>A new variable binding occurs as this variable is used more than once in the same pattern. In our original code, both <code>player_1</code> and <code>player_2</code> are used as variables in the <code>case</code> pattern. So always the first case is executed, which randomly fails if <code>player_2</code> is the winner but passes if not.</p>
<h3 id="using-the-pin-operator">Using the Pin Operator</h3>
<p>The pin operator (^) in Elixir is used to enforce a match against an existing variable&rsquo;s value, preventing variable rebinding. Let&rsquo;s revisit the modified test case that includes the pin operator:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">case</span> <span class="n">response</span><span class="p">[</span><span class="s2">&#34;winner&#34;</span><span class="p">][</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="o">^</span><span class="n">player_1</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">assert</span> <span class="n">response</span><span class="p">[</span><span class="s2">&#34;looser&#34;</span><span class="p">][</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span> <span class="o">=~</span> <span class="n">player_2</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="o">^</span><span class="n">player_2</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">assert</span> <span class="n">response</span><span class="p">[</span><span class="s2">&#34;looser&#34;</span><span class="p">][</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span> <span class="o">=~</span> <span class="n">player_1</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">raise</span> <span class="s2">&#34;Unexpected user_id&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">end</span></span></span></code></pre></div><p>By using the pin operator, we explicitly state that we want to match the value of player_1 and player_2 against the corresponding user IDs without introducing new bindings. This prevents any unintentional variable rebinding and ensures that our pattern matching behaves as expected.</p>
<h3 id="resources">Resources</h3>
<ul>
<li><a href="https://stackoverflow.com/questions/27971357/what-is-the-pin-operator-for-and-are-elixir-variables-mutable">What is the &ldquo;pin&rdquo; operator for, and are Elixir variables mutable?</a></li>
<li><a href="https://medium.com/@Julien_Corb/understand-the-pin-operator-in-elixir-a6f534d865a6">Understand the pin “^&quot; operator in Elixir</a></li>
</ul>
]]></content:encoded></item><item><title>Writing Unit Tests for Actix with Websockets</title><link>https://thacoon.com/blog/unit-test-websockets-in-rust-actix/</link><pubDate>Sun, 12 Nov 2023 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/unit-test-websockets-in-rust-actix/</guid><description>The Problem I am implementing a chat service using websockets based on Rust with Actix. Actix provides numerous examples, including a websocket-chat example, which I used as a reference for my implementation. However, I encountered difficulties while attempting to add my first unit test, following the testing documentation. Unfortunately, I couldn&amp;rsquo;t get it to work as I consistently received a response status of 400.
The Solution After thorough debugging, I discovered that I was missing some headers, specifically:</description><content:encoded><![CDATA[<h2 id="the-problem">The Problem</h2>
<p>I am implementing a chat service using websockets based on Rust with <a href="https://actix.rs/">Actix</a>. Actix provides numerous examples, including a websocket-chat example, which I used as a reference for my implementation. However, I encountered difficulties while attempting to add my first unit test, following the <a href="https://actix.rs/docs/testing">testing documentation</a>. Unfortunately, I couldn&rsquo;t get it to work as I consistently received a response status of 400.</p>
<h2 id="the-solution">The Solution</h2>
<p>After thorough debugging, I discovered that I was missing some headers, specifically:</p>
<ul>
<li><code>Upgrade: websocket</code></li>
<li><code>Connection: Upgrade</code></li>
<li><code>Sec-Websocket-Key: &lt;my-websocket-key&gt;</code></li>
<li><code>Sec-WEbSocket-Version: 13</code></li>
</ul>
<p>More information about these headers can be found on <a href="https://de.wikipedia.org/wiki/WebSocket">Websocket&rsquo;s Wikipedia page</a>.</p>
<p>Unlike in production scenarios, the value for Sec-Websocket-Key does not matter as long as the header exists.</p>
<p>In addition, I initially asserted <code>200 OK</code> using <code>assert!(resp.status().is_success());</code>, following the documentation. However, a successful response when connecting to the WebSocket is <code>101 Switching Protocols</code>, which I now verify using <code>assert_eq!(resp.status(), 101);</code>.</p>
<h2 id="the-code">The Code</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">  1</span><span class="cl"><span class="k">mod</span> <span class="nn">server</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="w"></span><span class="k">mod</span> <span class="nn">session</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">actix</span>::<span class="p">{</span><span class="n">Actor</span><span class="p">,</span><span class="w"> </span><span class="n">Addr</span><span class="p">,</span><span class="w"> </span><span class="n">StreamHandler</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">actix_web</span>::<span class="n">middleware</span>::<span class="n">Logger</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">actix_web</span>::<span class="p">{</span><span class="n">web</span><span class="p">,</span><span class="w"> </span><span class="n">App</span><span class="p">,</span><span class="w"> </span><span class="n">Error</span><span class="p">,</span><span class="w"> </span><span class="n">HttpRequest</span><span class="p">,</span><span class="w"> </span><span class="n">HttpResponse</span><span class="p">,</span><span class="w"> </span><span class="n">HttpServer</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">actix_web_actors</span>::<span class="n">ws</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">MyWs</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">Actor</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">MyWs</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="w">    </span><span class="k">type</span> <span class="nc">Context</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ws</span>::<span class="n">WebsocketContext</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">StreamHandler</span><span class="o">&lt;</span><span class="nb">Result</span><span class="o">&lt;</span><span class="n">ws</span>::<span class="n">Message</span><span class="p">,</span><span class="w"> </span><span class="n">ws</span>::<span class="n">ProtocolError</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">MyWs</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">handle</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">msg</span>: <span class="nb">Result</span><span class="o">&lt;</span><span class="n">ws</span>::<span class="n">Message</span><span class="p">,</span><span class="w"> </span><span class="n">ws</span>::<span class="n">ProtocolError</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="n">ctx</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="bp">Self</span>::<span class="n">Context</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="w">        </span><span class="k">match</span><span class="w"> </span><span class="n">msg</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="w">            </span><span class="nb">Ok</span><span class="p">(</span><span class="n">ws</span>::<span class="n">Message</span>::<span class="n">Ping</span><span class="p">(</span><span class="n">msg</span><span class="p">))</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">ctx</span><span class="p">.</span><span class="n">pong</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="w">            </span><span class="nb">Ok</span><span class="p">(</span><span class="n">ws</span>::<span class="n">Message</span>::<span class="n">Text</span><span class="p">(</span><span class="n">text</span><span class="p">))</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">ctx</span><span class="p">.</span><span class="n">text</span><span class="p">(</span><span class="n">text</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="w">            </span><span class="nb">Ok</span><span class="p">(</span><span class="n">ws</span>::<span class="n">Message</span>::<span class="n">Binary</span><span class="p">(</span><span class="n">bin</span><span class="p">))</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">ctx</span><span class="p">.</span><span class="n">binary</span><span class="p">(</span><span class="n">bin</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="w">            </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="w"></span><span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">chat_route</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="w">    </span><span class="n">req</span>: <span class="nc">HttpRequest</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="w">    </span><span class="n">stream</span>: <span class="nc">web</span>::<span class="n">Payload</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="w">    </span><span class="n">srv</span>: <span class="nc">web</span>::<span class="n">Data</span><span class="o">&lt;</span><span class="n">Addr</span><span class="o">&lt;</span><span class="n">server</span>::<span class="n">ChatServer</span><span class="o">&gt;&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="n">HttpResponse</span><span class="p">,</span><span class="w"> </span><span class="n">Error</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="w">    </span><span class="n">ws</span>::<span class="n">start</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="w">        </span><span class="n">session</span>::<span class="n">WsChatSession</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="w">            </span><span class="n">id</span>: <span class="mi">0</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="w">            </span><span class="n">room</span>: <span class="nb">Vec</span>::<span class="n">new</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="w">            </span><span class="n">addr</span>: <span class="nc">srv</span><span class="p">.</span><span class="n">get_ref</span><span class="p">().</span><span class="n">clone</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="w">        </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="w">        </span><span class="o">&amp;</span><span class="n">req</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="w">        </span><span class="n">stream</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="w">    </span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="w"></span><span class="cp">#[actix_web::main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="w"></span><span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">std</span>::<span class="n">io</span>::<span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="w">    </span><span class="n">env_logger</span>::<span class="n">init_from_env</span><span class="p">(</span><span class="n">env_logger</span>::<span class="n">Env</span>::<span class="n">new</span><span class="p">().</span><span class="n">default_filter_or</span><span class="p">(</span><span class="s">&#34;info&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">server</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">server</span>::<span class="n">ChatServer</span>::<span class="n">new</span><span class="p">().</span><span class="n">start</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="w">    </span><span class="n">log</span>::<span class="fm">info!</span><span class="p">(</span><span class="s">&#34;starting HTTP server at http://localhost:8080&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="w">    </span><span class="n">HttpServer</span>::<span class="n">new</span><span class="p">(</span><span class="k">move</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="w">        </span><span class="n">App</span>::<span class="n">new</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">app_data</span><span class="p">(</span><span class="n">web</span>::<span class="n">Data</span>::<span class="n">new</span><span class="p">(</span><span class="n">server</span><span class="p">.</span><span class="n">clone</span><span class="p">()))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">&#34;/ws/&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">web</span>::<span class="n">get</span><span class="p">().</span><span class="n">to</span><span class="p">(</span><span class="n">chat_route</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">wrap</span><span class="p">(</span><span class="n">Logger</span>::<span class="n">default</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="w">    </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">bind</span><span class="p">((</span><span class="s">&#34;127.0.0.1&#34;</span><span class="p">,</span><span class="w"> </span><span class="mi">8080</span><span class="p">))</span><span class="o">?</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">run</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="k">await</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="w"></span><span class="cp">#[cfg(test)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="w"></span><span class="k">mod</span> <span class="nn">tests</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="w">    </span><span class="k">use</span><span class="w"> </span><span class="k">super</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="w">    </span><span class="k">use</span><span class="w"> </span><span class="n">actix_web</span>::<span class="n">body</span>::<span class="n">MessageBody</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="w">    </span><span class="k">use</span><span class="w"> </span><span class="n">actix_web</span>::<span class="n">dev</span>::<span class="p">{</span><span class="n">ServiceFactory</span><span class="p">,</span><span class="w"> </span><span class="n">ServiceRequest</span><span class="p">,</span><span class="w"> </span><span class="n">ServiceResponse</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="w">    </span><span class="k">use</span><span class="w"> </span><span class="n">actix_web</span>::<span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="w">        </span><span class="n">http</span>::<span class="n">header</span>::<span class="n">ContentType</span><span class="p">,</span><span class="w"> </span><span class="n">http</span>::<span class="n">header</span>::<span class="n">HeaderValue</span><span class="p">,</span><span class="w"> </span><span class="n">http</span>::<span class="n">header</span>::<span class="no">CONNECTION</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="w">        </span><span class="n">http</span>::<span class="n">header</span>::<span class="no">SEC_WEBSOCKET_KEY</span><span class="p">,</span><span class="w"> </span><span class="n">http</span>::<span class="n">header</span>::<span class="no">SEC_WEBSOCKET_VERSION</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="w">        </span><span class="n">http</span>::<span class="n">header</span>::<span class="no">UPGRADE</span><span class="p">,</span><span class="w"> </span><span class="n">test</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="w">    </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">setup</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">App</span><span class="o">&lt;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="w">        </span><span class="k">impl</span><span class="w"> </span><span class="n">ServiceFactory</span><span class="o">&lt;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="w">            </span><span class="n">ServiceRequest</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="w">            </span><span class="n">Response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ServiceResponse</span><span class="o">&lt;</span><span class="k">impl</span><span class="w"> </span><span class="n">MessageBody</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="w">            </span><span class="n">Config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="w">            </span><span class="n">InitError</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="w">            </span><span class="n">Error</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Error</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="w">        </span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="w">    </span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="w">        </span><span class="n">env_logger</span>::<span class="n">init_from_env</span><span class="p">(</span><span class="n">env_logger</span>::<span class="n">Env</span>::<span class="n">new</span><span class="p">().</span><span class="n">default_filter_or</span><span class="p">(</span><span class="s">&#34;info&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">server</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">server</span>::<span class="n">ChatServer</span>::<span class="n">new</span><span class="p">().</span><span class="n">start</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="w">        </span><span class="n">App</span>::<span class="n">new</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">app_data</span><span class="p">(</span><span class="n">web</span>::<span class="n">Data</span>::<span class="n">new</span><span class="p">(</span><span class="n">server</span><span class="p">.</span><span class="n">clone</span><span class="p">()))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">&#34;/ws/&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">web</span>::<span class="n">get</span><span class="p">().</span><span class="n">to</span><span class="p">(</span><span class="n">chat_route</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">wrap</span><span class="p">(</span><span class="n">Logger</span>::<span class="n">default</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="w">    </span><span class="cp">#[actix_web::test]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="w">    </span><span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">test_unauthorized_without_authorization_header</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">app</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">test</span>::<span class="n">init_service</span><span class="p">(</span><span class="n">setup</span><span class="p">()).</span><span class="k">await</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">req</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">test</span>::<span class="n">TestRequest</span>::<span class="n">get</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">uri</span><span class="p">(</span><span class="s">&#34;/ws/&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">insert_header</span><span class="p">((</span><span class="no">UPGRADE</span><span class="p">,</span><span class="w"> </span><span class="n">HeaderValue</span>::<span class="n">from_static</span><span class="p">(</span><span class="s">&#34;websocket&#34;</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">insert_header</span><span class="p">((</span><span class="no">CONNECTION</span><span class="p">,</span><span class="w"> </span><span class="n">HeaderValue</span>::<span class="n">from_static</span><span class="p">(</span><span class="s">&#34;Upgrade&#34;</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">insert_header</span><span class="p">((</span><span class="no">SEC_WEBSOCKET_VERSION</span><span class="p">,</span><span class="w"> </span><span class="n">HeaderValue</span>::<span class="n">from_static</span><span class="p">(</span><span class="s">&#34;13&#34;</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">insert_header</span><span class="p">((</span><span class="w">
</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="w">                </span><span class="no">SEC_WEBSOCKET_KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="w">                </span><span class="n">HeaderValue</span>::<span class="n">from_static</span><span class="p">(</span><span class="s">&#34;does-not-matter-for-testing&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="w">            </span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">insert_header</span><span class="p">(</span><span class="n">ContentType</span>::<span class="n">plaintext</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">to_request</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">108</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">resp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">test</span>::<span class="n">call_service</span><span class="p">(</span><span class="o">&amp;</span><span class="n">app</span><span class="p">,</span><span class="w"> </span><span class="n">req</span><span class="p">).</span><span class="k">await</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="w">        </span><span class="fm">assert!</span><span class="p">(</span><span class="n">resp</span><span class="p">.</span><span class="n">status</span><span class="p">().</span><span class="n">is_informational</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="ln">111</span><span class="cl"><span class="w">        </span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">resp</span><span class="p">.</span><span class="n">status</span><span class="p">(),</span><span class="w"> </span><span class="mi">101</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">112</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">113</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h2 id="lessons-learned">Lessons Learned</h2>
<h3 id="return-app-from-a-function">Return App from a function</h3>
<p>The return type is as follow:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">App</span><span class="o">&lt;</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">        </span><span class="k">impl</span><span class="w"> </span><span class="n">ServiceFactory</span><span class="o">&lt;</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">            </span><span class="n">ServiceRequest</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">            </span><span class="n">Response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ServiceResponse</span><span class="o">&lt;</span><span class="k">impl</span><span class="w"> </span><span class="n">MessageBody</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">            </span><span class="n">Config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">            </span><span class="n">InitError</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">            </span><span class="n">Error</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Error</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w">        </span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="w">    </span><span class="o">&gt;</span></span></span></code></pre></div><p>Taken from here: <a href="https://github.com/actix/actix-web/wiki/FAQ#how-can-i-return-app-from-a-function--why-is-appentry-private">How can I return App from a function / why is AppEntry private?</a>, which links to the test code <a href="https://github.com/actix/actix-web/blob/b1c85ba85be91b5ea34f31264853b411fadce1ef/actix-web/src/app.rs#L698">here</a>.</p>
<h3 id="unhide-test-output">Unhide test output</h3>
<p>By default, the Rust test suite hides output from test execution, which prevented me from debugging this. To display all output, you need to run it as follows:  <code>cargo test -- --nocapture</code></p>
<p>As explained in the display options section in <a href="https://doc.rust-lang.org/cargo/commands/cargo-test.html#display-options">The Cargo Book</a>.</p>
]]></content:encoded></item><item><title>Bash script for easy Python requirements management</title><link>https://thacoon.com/blog/easy-python-requirements/</link><pubDate>Tue, 08 Jun 2021 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/easy-python-requirements/</guid><description>Update I migrated to Pipenv some time ago and being pretty happy about this.
Outdated article For every Python project I set up a virtualenv and handle my requirements with pip-tools. It is a command line tool to keep pip-based packages fresh and pinns them. It is my favorite tool to handle Pyhon requirements. Using pip-tools you basically define your requirements in a *.infile and when you run pip-compile it will create a *.</description><content:encoded><![CDATA[<h2 id="update">Update</h2>
<p>I migrated to <a href="https://pipenv.pypa.io/en/latest/">Pipenv</a> some time ago and being pretty happy about this.</p>
<h2 id="outdated-article">Outdated article</h2>
<p>For every Python project I set up a virtualenv and handle my requirements with <code>pip-tools</code>. It is a command line tool to keep <code>pip</code>-based packages fresh and pinns them. It is my favorite tool to handle Pyhon requirements. Using <code>pip-tools</code> you basically define your requirements in a <code>*.in</code>file and when you run <code>pip-compile</code> it will create a <code>*.txt</code>requirements files with all dependencies (and all underlying dependencies) pinned. And using <code>pip-sync</code> it will install all packages definied in a requirements file but also uninstall any other package which is not defined.</p>
<p>In most cases I split my requirements files into four categories: base, dev, tests and production. When developing I often have all requirements installed, in my CI only base and tests requirements and on production only base and production requirements.</p>
<p>So over time I approximately came to the following script, that lies as <code>update.sh</code> in the root of my Python projects. On a fresh virtualenv you first need to run <code>pip install pip-tools</code>, change the file permissions <code>chmod 744 update.sh</code> and then the following script can be used.</p>
<p>What it simply does when you run <code>./update.sh compile</code> it will use <code>pip-tools</code> to create your pinned requirements files.</p>
<p>And then you can install the different combinations with <code>./update.sh sync-all</code> for all packages, <code>./update.sh sync-tests</code> for base and test and <code>./update.sh sync-dev</code> for base, test and dev.</p>
<p>There are also commands which do not require <code>pip-tools</code>to be installed, these use <code>pip</code>to install all needed dependencies. This is useful for CI environments or production where you install the dependencies only once and dont want unneded extra tools to be installed. You can run <code>./update sync-ci</code>in the CI to install base and test or <code>./update sync-production</code>to install base and production requirements.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">function</span> <span class="nb">help</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;usage: ./update arguments&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;  compile                 Upgrades the pinned version of all packages.&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;  sync-all                Update your virtual environment of all packages.&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;  sync-dev                Update your virtual environment for base, dev and test.&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;  sync-tests              Update your virtual environment for base and test.&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;  sync-ci                 Update your virtual environment for base and test.&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;  sync-production         Update your virtual environment for base and production.&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;  upgrade                 Upgrades pip, setuptools and pip-tools.&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="nv">$#</span> -eq  <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="nb">help</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><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;sync-production&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    pip install --no-cache-dir -r requirements/production.txt
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">elif</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;sync-ci&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    pip install --no-cache-dir -r requirements/tests.txt
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">elif</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;upgrade&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    pip install --upgrade pip setuptools pip-tools
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">if</span> ! pip list <span class="p">|</span> grep <span class="s2">&#34;pip-tools&#34;</span> &gt; /dev/null <span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;This action requires pip-tools. Run &#39;pip install pip-tools&#39; or &#39;./update.sh upgrade&#39;.&#34;</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="nb">help</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><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;sync-all&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    pip-sync requirements/base.txt requirements/dev.txt requirements/tests.txt requirements/production.txt
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="k">elif</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;sync-dev&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    pip-sync requirements/base.txt requirements/dev.txt requirements/tests.txt
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="k">elif</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;sync-tests&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">    pip-sync requirements/base.txt requirements/tests.txt
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="k">elif</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;compile&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    pip-compile --upgrade requirements/base.in -o requirements/base.txt
</span></span><span class="line"><span class="ln">50</span><span class="cl">    pip-compile --upgrade requirements/base.in requirements/tests.in requirements/dev.in -o requirements/dev.txt
</span></span><span class="line"><span class="ln">51</span><span class="cl">    pip-compile --upgrade requirements/base.in requirements/tests.in -o requirements/tests.txt
</span></span><span class="line"><span class="ln">52</span><span class="cl">    pip-compile --upgrade requirements/base.in requirements/production.in -o requirements/production.txt
</span></span><span class="line"><span class="ln">53</span><span class="cl">
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">    <span class="nb">help</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="k">fi</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/jazzband/pip-tools/">pip-tools</a></li>
</ul>
]]></content:encoded></item><item><title>Test gqlgen resolvers that rely on context values</title><link>https://thacoon.com/blog/gqlgen/</link><pubDate>Tue, 01 Jun 2021 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/gqlgen/</guid><description>The problem You are using gqlgen for building GraphQL servers in your Go projects. But your queryResolver or mutationResolver rely on some context value. For example as you are using gin and add values to your request context with middlewares. Therefore you need a way to modify the context of the gqlgen client in your tests.
An example resolver code could look like the following.
1func (r *mutationResolver) CreateCategory(ctx context.Context, title string) (*model.</description><content:encoded><![CDATA[<h2 id="the-problem">The problem</h2>
<p>You are using <a href="https://github.com/99designs/gqlgen">gqlgen</a> for building GraphQL servers in your Go projects. But your <code>queryResolver</code> or <code>mutationResolver</code> rely on some context value. For example as you are using gin and add values to your request context with middlewares. Therefore you need a way to modify the context of the gqlgen client in your tests.</p>
<p>An example resolver code could look like the following.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">mutationResolver</span><span class="p">)</span> <span class="nf">CreateCategory</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">title</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">model</span><span class="p">.</span><span class="nx">Category</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="nx">currentUser</span> <span class="o">:=</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Value</span><span class="p">(</span><span class="s">&#34;User&#34;</span><span class="p">).(</span><span class="nx">db</span><span class="p">.</span><span class="nx">User</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="k">return</span> <span class="nx">result</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="the-solution">The solution</h2>
<p>The following code shows you how to add values to context when testing gqlgen resolvers with the client.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Add param variables to context of client request
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// The gqlgen client accepts functional options as the following one
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">addContext</span><span class="p">(</span><span class="nx">user</span> <span class="nx">db</span><span class="p">.</span><span class="nx">User</span><span class="p">)</span> <span class="nx">client</span><span class="p">.</span><span class="nx">Option</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">bd</span> <span class="o">*</span><span class="nx">client</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="nx">ctx</span> <span class="p">=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithValue</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;User&#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="nx">bd</span><span class="p">.</span><span class="nx">HTTP</span> <span class="p">=</span> <span class="nx">bd</span><span class="p">.</span><span class="nx">HTTP</span><span class="p">.</span><span class="nf">WithContext</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// Example tests
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">TestMutationResolver</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1">// Create the gqlgen client for testing
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>    <span class="nx">h</span> <span class="o">:=</span> <span class="nx">handler</span><span class="p">.</span><span class="nf">NewDefaultServer</span><span class="p">(</span><span class="nx">generated</span><span class="p">.</span><span class="nf">NewExecutableSchema</span><span class="p">(</span><span class="nx">generated</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="nx">Resolvers</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">Resolver</span><span class="p">{}}))</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="nx">c</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">h</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="c1">// Example test
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>    <span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;Create category&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="c1">// struct that saves the response of the mutation below
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span>        <span class="kd">var</span> <span class="nx">resp</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">            <span class="nx">CreateCategory</span> <span class="kd">struct</span><span class="p">{</span> <span class="nx">Title</span> <span class="kt">string</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="c1">// Create the variable that is passed to addContext
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span>        <span class="nx">user</span> <span class="o">:=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">User</span><span class="p">{}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="c1">// Call the resolver via the client and modify the context via functional options
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1"></span>        <span class="nx">c</span><span class="p">.</span><span class="nf">MustPost</span><span class="p">(</span><span class="s">`
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="s">            mutation($title: String!) {
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="s">                createCategory(title:$title) {
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="s">                    title
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="s">                }
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="s">            }`</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">            <span class="o">&amp;</span><span class="nx">resp</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">            <span class="nx">client</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="s">&#34;title&#34;</span><span class="p">,</span> <span class="nx">category</span><span class="p">.</span><span class="nx">Title</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">            <span class="nf">addContext</span><span class="p">(</span><span class="nx">user</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl">        <span class="c1">// an example assertion
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="c1"></span>        <span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="s">&#34;Awesome blogs&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">CreateCategory</span><span class="p">.</span><span class="nx">Title</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://www.sohamkamani.com/golang/options-pattern/">Awesome blog post about functional options in Go</a></li>
<li><a href="https://github.com/99designs/gqlgen">gqlgen is a Go library for building GraphQL servers</a></li>
<li><a href="https://github.com/gin-gonic/gin">Gin is a web framework written in Go</a></li>
</ul>
]]></content:encoded></item><item><title>Update a column based on row_number() or other tables</title><link>https://thacoon.com/blog/sql-update-based-on-row-id/</link><pubDate>Sun, 02 May 2021 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/sql-update-based-on-row-id/</guid><description>The problem On some project I inserted a new column called position so the entries are orderable by the users. On this project users can create categories which have e.g. a title and may have a super/parent category and are owned by the user who created them.
The problem was that I wanted to have an initial alphabetical ordering of the categories and some rules had to apply. For the sorting only categories of the same user are considered and only categories that have a common super category.</description><content:encoded><![CDATA[<h2 id="the-problem">The problem</h2>
<p>On some project I inserted a new column called <code>position</code> so the entries are orderable by the users. On this project users can create categories which have e.g. a title and may have a super/parent category and are owned by the user who created them.</p>
<p>The problem was that I wanted to have an initial alphabetical ordering of the categories and some rules had to apply. For the sorting only categories of the same user are considered and only categories that have a common super category.</p>
<p>The table looked similar to the following, however note that I have left out some columns and changed the data types (e.g. int as id instead of uuid) to make it more redeable.</p>
<table>
<thead>
<tr>
<th>id</th>
<th>title</th>
<th>&hellip;</th>
<th>super_category_id</th>
<th>user_ref</th>
<th>position</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>A</td>
<td></td>
<td>NULL</td>
<td>user_1</td>
<td>0</td>
</tr>
<tr>
<td>2</td>
<td>C</td>
<td></td>
<td>NULL</td>
<td>user_2</td>
<td>0</td>
</tr>
<tr>
<td>3</td>
<td>B</td>
<td></td>
<td>NULL</td>
<td>user_2</td>
<td>0</td>
</tr>
<tr>
<td>4</td>
<td>BB</td>
<td></td>
<td>1</td>
<td>user_1</td>
<td>0</td>
</tr>
<tr>
<td>5</td>
<td>AA</td>
<td></td>
<td>1</td>
<td>user_1</td>
<td>0</td>
</tr>
<tr>
<td>6</td>
<td>CC</td>
<td></td>
<td>2</td>
<td>user_2</td>
<td>0</td>
</tr>
</tbody>
</table>
<h2 id="the-solution">The solution</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">update</span><span class="w"> </span><span class="n">categories</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">	</span><span class="k">set</span><span class="w"> </span><span class="k">position</span><span class="o">=</span><span class="n">r</span><span class="p">.</span><span class="n">row_id</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="k">from</span><span class="w"> </span><span class="p">(</span><span class="k">select</span><span class="w"> </span><span class="k">c</span><span class="p">.</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">row_number</span><span class="p">()</span><span class="w"> </span><span class="n">over</span><span class="w"> </span><span class="p">(</span><span class="n">partition</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="k">c</span><span class="p">.</span><span class="n">super_category_id</span><span class="p">,</span><span class="w"> </span><span class="k">c</span><span class="p">.</span><span class="n">user_ref</span><span class="w"> </span><span class="k">order</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="k">c</span><span class="p">.</span><span class="n">title</span><span class="w"> </span><span class="k">asc</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">row_id</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">categories</span><span class="w"> </span><span class="k">c</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">r</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"></span><span class="k">where</span><span class="w"> </span><span class="n">categories</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">id</span><span class="p">;</span></span></span></code></pre></div><p>This sql query was the solution and would result in a table as seen below.</p>
<table>
<thead>
<tr>
<th>id</th>
<th>title</th>
<th>&hellip;</th>
<th>super_category_id</th>
<th>user_ref</th>
<th>position</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>A</td>
<td></td>
<td>NULL</td>
<td>user_1</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>C</td>
<td></td>
<td>NULL</td>
<td>user_2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>B</td>
<td></td>
<td>NULL</td>
<td>user_2</td>
<td>1</td>
</tr>
<tr>
<td>4</td>
<td>BB</td>
<td></td>
<td>1</td>
<td>user_1</td>
<td>2</td>
</tr>
<tr>
<td>5</td>
<td>AA</td>
<td></td>
<td>1</td>
<td>user_1</td>
<td>1</td>
</tr>
<tr>
<td>6</td>
<td>CC</td>
<td></td>
<td>2</td>
<td>user_2</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>The inner select query divides the query&rsquo;s result into partions. <code>partition by c.super_category_id, c.user_ref order by c.title asc</code> divides the query&rsquo;s result set into partions, every partion has a unique <code>super_category_id</code> and <code>user_ref</code> and is in itself ordered alphabetically.</p>
<p>Using <code>row_numer()</code> we get the row number within the partion for every column and update the position column accordingly in the table.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/30505210/update-a-column-based-on-row-number">Update a column based on row_number()</a></li>
<li><a href="https://www.sqltutorial.org/sql-window-functions/sql-partition-by/">SQL PARTITION BY</a></li>
</ul>
]]></content:encoded></item><item><title>Use Rocket's managed state within request guards</title><link>https://thacoon.com/blog/rust-rocket-use-config-in-guard/</link><pubDate>Sun, 04 Apr 2021 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/rust-rocket-use-config-in-guard/</guid><description>The problem Request guards represents some validation policy and is one of Rocket&amp;rsquo;s most powerful instruments. You can read more about guards in the docs. For example you can use guards to add authentication to handlers, there is even a nice example in the api doc. However, for simplification the valid api key is hard coded in the example. But we dont want this in a real project, so the problem is that we want to have a configurable api key.</description><content:encoded><![CDATA[<h2 id="the-problem">The problem</h2>
<p>Request guards represents some validation policy and is one of Rocket&rsquo;s most powerful instruments. You can read more about guards in the <a href="https://rocket.rs/master/guide/requests/#request-guards">docs</a>. For example you can use guards to add authentication to handlers, there is even a nice example in the <a href="https://api.rocket.rs/master/rocket/request/trait.FromRequest.html#example-1">api doc</a>. However, for simplification the valid api key is hard coded in the example. But we dont want this in a real project, so the problem is that we want to have a configurable api key.</p>
<h2 id="the-solution">The solution</h2>
<p>One way to have a configurable api key is by using <a href="https://rocket.rs/master/guide/configuration/">Rocket&rsquo;s configuration</a> abilities. You define a <code>Rocket.toml</code> that is used to configure Rocket but you can also add additional configuration parameters, like <code>api_token</code>. This file is committed so you cannot have your production secrets in it. In production you would overwrite your secrets with <code>ROCKET_</code>env variables, as these have a higher priority than the <code>Rocket.toml</code>file.</p>
<p>Then you can use <code>AdHoc::config()</code> to store a typed config in managed state.</p>
<p>And then retrieve it with <code>request.rocket().state::&lt;Config&gt;()</code>.</p>
<p>The following code shows this in action based on the example taken from Rocket&rsquo;s docs.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// src/main.rs
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="cp">#[macro_use]</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="k">crate</span><span class="w"> </span><span class="n">rocket</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">rocket</span>::<span class="n">http</span>::<span class="n">Status</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">rocket</span>::<span class="n">request</span>::<span class="p">{</span><span class="n">Outcome</span><span class="p">,</span><span class="w"> </span><span class="n">Request</span><span class="p">,</span><span class="w"> </span><span class="n">FromRequest</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">rocket</span>::<span class="n">State</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">rocket</span>::<span class="n">fairing</span>::<span class="n">AdHoc</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">serde</span>::<span class="n">Deserialize</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Deserialize)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">Config</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="n">api_key</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">ApiKey</span><span class="o">&lt;</span><span class="na">&#39;r</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="na">&#39;r</span><span class="w"> </span><span class="kt">str</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Debug)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"></span><span class="k">enum</span> <span class="nc">ApiKeyError</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="n">Missing</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="n">Invalid</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w"></span><span class="cp">#[rocket::async_trait]</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="o">&lt;</span><span class="na">&#39;r</span><span class="o">&gt;</span><span class="w"> </span><span class="n">FromRequest</span><span class="o">&lt;</span><span class="na">&#39;r</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">ApiKey</span><span class="o">&lt;</span><span class="na">&#39;r</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">    </span><span class="k">type</span> <span class="nc">Error</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ApiKeyError</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">    </span><span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_request</span><span class="p">(</span><span class="n">req</span>: <span class="kp">&amp;</span><span class="na">&#39;r</span> <span class="nc">Request</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Outcome</span><span class="o">&lt;</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="bp">Self</span>::<span class="n">Error</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">        </span><span class="c1">// Retrieve the config state like this
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">req</span><span class="p">.</span><span class="n">rocket</span><span class="p">().</span><span class="n">state</span>::<span class="o">&lt;</span><span class="n">Config</span><span class="o">&gt;</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="k">fn</span> <span class="nf">is_valid</span><span class="p">(</span><span class="n">key</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">api_key</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">            </span><span class="n">key</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">api_key</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">        </span><span class="k">match</span><span class="w"> </span><span class="n">req</span><span class="p">.</span><span class="n">headers</span><span class="p">().</span><span class="n">get_one</span><span class="p">(</span><span class="s">&#34;Authorization&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">            </span><span class="nb">None</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Outcome</span>::<span class="n">Failure</span><span class="p">((</span><span class="n">Status</span>::<span class="n">Unauthorized</span><span class="p">,</span><span class="w"> </span><span class="n">ApiKeyError</span>::<span class="n">Missing</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">            </span><span class="nb">Some</span><span class="p">(</span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">is_valid</span><span class="p">(</span><span class="n">key</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">config</span><span class="p">.</span><span class="n">api_key</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Outcome</span>::<span class="n">Success</span><span class="p">(</span><span class="n">ApiKey</span><span class="p">(</span><span class="n">key</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">            </span><span class="nb">Some</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">Outcome</span>::<span class="n">Failure</span><span class="p">((</span><span class="n">Status</span>::<span class="n">Unauthorized</span><span class="p">,</span><span class="w"> </span><span class="n">ApiKeyError</span>::<span class="n">Invalid</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w"></span><span class="cp">#[get(</span><span class="s">&#34;/&#34;</span><span class="cp">)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w"></span><span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">index</span><span class="p">(</span><span class="n">_config</span>: <span class="nc">State</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="p">,</span><span class="w"> </span><span class="n">Config</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="n">_key</span>: <span class="nc">ApiKey</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kp">&amp;</span><span class="nb">&#39;static</span> <span class="kt">str</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">    </span><span class="s">&#34;Hello, world!&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">rocket</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">rocket</span>::<span class="n">Rocket</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">rocket</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rocket</span>::<span class="n">ignite</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">figment</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rocket</span><span class="p">.</span><span class="n">figment</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">_config</span>: <span class="nc">Config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">figment</span><span class="p">.</span><span class="n">extract</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;config&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">    </span><span class="c1">// Store the typed config in managed state
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">rocket</span><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">mount</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span><span class="w"> </span><span class="fm">routes!</span><span class="p">[</span><span class="n">index</span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">attach</span><span class="p">(</span><span class="n">AdHoc</span>::<span class="n">config</span>::<span class="o">&lt;</span><span class="n">Config</span><span class="o">&gt;</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="w"></span><span class="cp">#[rocket::main]</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w"></span><span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">    </span><span class="n">rocket</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">launch</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="k">await</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># Cargo.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;example&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2018&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c"># Need latest version of Rocket (min. 5.0.0) as guard with async_trait is used</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">rocket</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/SergioBenitez/Rocket&#34;</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.5.0-dev&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">serde</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;derive&#34;</span><span class="p">]</span> <span class="p">}</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># Rocket.toml</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">[</span><span class="nx">default</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">api_key</span> <span class="p">=</span> <span class="s2">&#34;secret-api-key&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">[</span><span class="nx">debug</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">api_key</span> <span class="p">=</span> <span class="s2">&#34;secret-api-key&#34;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">[</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="nx">api_key</span> <span class="p">=</span> <span class="s2">&#34;secret-api-key&#34;</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/SergioBenitez/Rocket">Rocket</a></li>
<li><a href="https://rocket.rs/master/guide/requests/#request-guards">Rocket&rsquo;s Request Guards</a></li>
<li><a href="https://api.rocket.rs/master/rocket/request/trait.FromRequest.html#example-1">Rocket&rsquo;s Request Guard Example</a></li>
<li><a href="https://rocket.rs/master/guide/configuration/">Rockets&rsquo;s Docs for Configuration</a></li>
</ul>
]]></content:encoded></item><item><title>Rocket (Rust) - Trait is not implemented compilation error for a type that it has implemented</title><link>https://thacoon.com/blog/rust-trait-compilation-error/</link><pubDate>Wed, 31 Mar 2021 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/rust-trait-compilation-error/</guid><description>The problem I needed to use the 0.5.0-dev version of Rocket as I wanted to use the new async features. As this version is not yet published you need to point Cargo to a git repository. However, I needed some functionality of rocket_contrib, the Uuuid, but I was very confused on how to add it to the dependencies correctly. As I am currently very new on Rust, Rocket and the documentation did not properly explained it.</description><content:encoded><![CDATA[<h2 id="the-problem">The problem</h2>
<p>I needed to use the 0.5.0-dev version of <a href="https://rocket.rs/">Rocket</a> as I wanted to use the new async features. As this version is not yet published you need to point Cargo to a git repository. However, I needed some functionality of <code>rocket_contrib</code>, the Uuuid,  but I was very confused on how to add it to the dependencies correctly. As I am currently very new on Rust, Rocket and the documentation did not properly explained it. I got the following error when compiling:</p>





<pre tabindex="0"><code>the trait `FromFormField&lt;&#39;_&gt;` is not implemented for `rocket_contrib::uuid::Uuid`</code></pre><p>At this point I was very confused as Uuid implements the FromFormField according to the <a href="https://api.rocket.rs/master/rocket_contrib/uuid/struct.Uuid.html">docs</a>. But the problem was that I did not properly configured my <code>Cargo.toml</code></p>
<h2 id="the-solution">The solution</h2>
<p>The correct <code>Cargo.toml</code> would be the following</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;example&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2018&#34;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nx">rocket</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/SergioBenitez/Rocket&#34;</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.5.0-dev&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="nx">rocket_contrib</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/SergioBenitez/Rocket&#34;</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.5.0-dev&#34;</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;uuid&#34;</span><span class="p">]</span> <span class="p">}</span></span></span></code></pre></div><p>Or the following would also be correct</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;example&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2018&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">rocket</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/SergioBenitez/Rocket&#34;</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.5.0-dev&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">.</span><span class="nx">rocket_contrib</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/SergioBenitez/Rocket&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.5.0-dev&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;uuid&#34;</span><span class="p">]</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/SergioBenitez/Rocket/issues/1572">GitHub Issue</a></li>
<li><a href="https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html">Rust Book - Specifying Dependencies</a></li>
</ul>
]]></content:encoded></item><item><title>Launch jupyter lab with specific Python Version via docker</title><link>https://thacoon.com/blog/launch-jupyter-lab-from-docker/</link><pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/launch-jupyter-lab-from-docker/</guid><description>Build jupyter lab image At first we create a custom jupyter lab docker image. Create a file called Dockerfile and add the following content:
1FROM python:3.8 2 3EXPOSE 8888 4 5RUN pip install --no-cache-dir jupyterlab \ 6 &amp;amp;&amp;amp; useradd -ms /bin/bash lab 7 8USER lab 9 10WORKDIR /home/lab 11 12CMD jupyter lab --ip=0.0.0.0Now using this Dockerfile we build an image with sudo docker build -t jupyter-lab .
Run Image You can start a jupyter lab instance with sudo docker run -it --name lab -p 8888:8888 -v &amp;quot;/change/local/path:/home/lab&amp;quot; jupyter-lab to which you can normally connect.</description><content:encoded><![CDATA[<h2 id="build-jupyter-lab-image">Build jupyter lab image</h2>
<p>At first we create a custom jupyter lab docker image. Create a file called <code>Dockerfile</code> and add the following content:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">FROM</span><span class="s"> python:3.8</span><span class="err">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="s"> 8888</span><span class="err">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="err"></span><span class="k">RUN</span> pip install --no-cache-dir jupyterlab <span class="se">\
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="se"></span>    <span class="o">&amp;&amp;</span> useradd -ms /bin/bash lab<span class="err">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="err"></span><span class="k">USER</span><span class="s"> lab</span><span class="err">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="s"> /home/lab</span><span class="err">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="err"></span><span class="k">CMD</span> jupyter lab --ip<span class="o">=</span>0.0.0.0</span></span></code></pre></div><p>Now using this Dockerfile we build an image with <code>sudo docker build -t jupyter-lab .</code></p>
<h2 id="run-image">Run Image</h2>
<p>You can start a jupyter lab instance with <code>sudo docker run -it --name lab -p 8888:8888 -v &quot;/change/local/path:/home/lab&quot; jupyter-lab</code> to which you can normally connect. This will start a container named <code>lab</code>, exposes jupyter lab to your host and mounts <code>/change/local/path</code>. If you want to automatically remove the container after stopping you can additionally add <code>--rm</code> to the command.</p>
<p>When you stopped the container and did not removed it you can restart it with <code>sudo docker start lab -i</code>.</p>
<h2 id="run-binbash">Run /bin/bash</h2>
<p>In a second terminal you can connect to the running container with <code>sudo docker exec -it lab /bin/bash</code>. This allows you to run aditional commands like <code>pip install tensorflow</code>.</p>
]]></content:encoded></item><item><title>Default Props in React and Typescript</title><link>https://thacoon.com/blog/react-typescript-default-props/</link><pubDate>Mon, 21 Dec 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/react-typescript-default-props/</guid><description>Lately I started to do some frontend related stuff and started working with React. At the start when using JS I was using PropTypes for typechecking. When using Proptypes it is easy to create optional properties you just need to add them to defaultProps. Later I switched to typescript to get better type checks in the whole codebase and a better developer experience. But when I came to the point where I needed to have an property with some default I could not find any easy to understand solution.</description><content:encoded><![CDATA[<p>Lately I started to do some frontend related stuff and started working with React. At the start when using JS I was using PropTypes for typechecking. When using Proptypes it is easy to create optional properties you just need to add them to <code>defaultProps</code>. Later I switched to typescript to get better type checks in the whole codebase and a better developer experience. But when I came to the point where I needed to have an property with some default I could not find any easy to understand solution. But today I found a easy solution on this <a href="https://github.com/microsoft/TypeScript/issues/31247#issuecomment-489962705-permalink">GitHub issue</a>, for <a href="https://github.com/microsoft/TypeScript/issues/31247#issuecomment-489962705-permalink">Typescript 3.1 or above</a>. The following example shows you how to make a default property in React with Typescript.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="kr">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">interface</span> <span class="nx">Props</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="nx">greeting?</span>: <span class="kt">string</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="nx">planet</span>: <span class="kt">string</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="kr">const</span> <span class="nx">Greet</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span>: <span class="kt">Props</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">greeting</span><span class="p">,</span> <span class="nx">planet</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">props</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">      <span class="p">{</span><span class="sb">`</span><span class="si">${</span><span class="nx">greeting</span><span class="si">}</span><span class="sb"> from </span><span class="si">${</span><span class="nx">planet</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nx">Greet</span><span class="p">.</span><span class="nx">defaultProps</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="nx">greeting</span><span class="o">:</span> <span class="s1">&#39;Hello&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">}</span> <span class="kr">as</span> <span class="nx">Partial</span><span class="p">&lt;</span><span class="nt">Props</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="kr">const</span> <span class="nx">component</span> <span class="o">=</span> <span class="p">&lt;</span><span class="nt">Greet</span> <span class="na">planet</span><span class="o">=</span><span class="s">&#34;Earth&#34;</span> <span class="p">/&gt;</span></span></span></code></pre></div><p>The <code>as Partial&lt;Props&gt;</code> is not necessary but it is a good practice as without it you could use wrong type as a default prop and TypeScript wouldn&rsquo;t complain.</p>
<h2 id="references">References</h2>
<h3 id="resources">Resources</h3>
<ul>
<li><a href="https://github.com/microsoft/TypeScript/issues/31247#issuecomment-489962705-permalink">GitHub Issue</a></li>
</ul>
]]></content:encoded></item><item><title>Developing a browser extension with Create React App and Tailwind CSS</title><link>https://thacoon.com/blog/create-react-app-browser-extension-with-tailwind/</link><pubDate>Mon, 21 Dec 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/create-react-app-browser-extension-with-tailwind/</guid><description>1. Create new app Create a new app via create-react-app with the following command.
npx create-react-app my-browser-extension --template typescriptOr for Typescrip support run this command:
npx create-react-app my-browser-extension --template typescript2. Setup the manifest Create React App creates a public dir, delete everything except the manifets.json and index.html. The current manifest is for Web Apps, but we need a Web Extensions manifest. So replace the public/manifest.json content with for example the following one.</description><content:encoded><![CDATA[<h2 id="1-create-new-app">1. Create new app</h2>
<p>Create a new app via <code>create-react-app</code> with the following command.</p>





<pre tabindex="0"><code>npx create-react-app my-browser-extension --template typescript</code></pre><p>Or for Typescrip support run this command:</p>





<pre tabindex="0"><code>npx create-react-app my-browser-extension --template typescript</code></pre><h2 id="2-setup-the-manifest">2. Setup the manifest</h2>
<p>Create React App creates a <code>public</code> dir, delete everything except the <code>manifets.json</code> and <code>index.html</code>. The current manifest is for Web Apps, but we need a <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json">Web Extensions manifest</a>. So replace the <code>public/manifest.json</code> content with for example the following one. An extension should have an icon, for the following manifest you would need to add a svg logo in the <code>public</code> dir.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nt">&#34;manifest_version&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;My-Browser-Extension&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;My browser extension&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="nt">&#34;homepage_url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://example.org&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="nt">&#34;icons&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="nt">&#34;48&#34;</span><span class="p">:</span> <span class="s2">&#34;logo.svg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="nt">&#34;96&#34;</span><span class="p">:</span> <span class="s2">&#34;logo.svg&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="nt">&#34;permissions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="nt">&#34;web_accessible_resources&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="3-setup-tailwind-css">3. Setup Tailwind CSS</h2>
<p>Install the Tailwind and its peer-dependencies using <code>npm</code>. As Create React App doesn&rsquo;t support PostCSS 8 yet we need to install the Tailwind CSS v2.0 PostCSS 7 compatibility build.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9 @craco/craco</span></span></code></pre></div><p>We also installed <code>@craco/craco</code>, because Create React App doesn&rsquo;t let you override the PostCSS configuration natively. Therefore we need to replace <code>react-scripts</code> with <code>craco</code> in the <code>package.json</code> like so:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="c1">//...
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>    <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nt">&#34;start&#34;</span><span class="p">:</span> <span class="s2">&#34;craco start&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;craco build&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt">&#34;test&#34;</span><span class="p">:</span> <span class="s2">&#34;craco test&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nt">&#34;eject&#34;</span><span class="p">:</span> <span class="s2">&#34;react-scripts eject&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">//...
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>Then, generate your tailwind config via <code>npx tailwindcss init</code>. In the <code>tailwind.config.js</code> file you need to change the line <code>purge: []</code> to <code>purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html']</code>.</p>
<p>Finally include Tailwind in your <code>src/index.css</code></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="ln">1</span><span class="cl"><span class="c">/* src/index.css */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">base</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">components</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">utilities</span><span class="p">;</span></span></span></code></pre></div><h2 id="4-setup-for-production">4. Setup for production</h2>
<p>Create React App embeds an inline script into <code>index.html</code> in the production build. But this would break the extension usage as it goes against its CSP (Content Security Policy), so we just disable it.</p>
<p>Change the build script in <code>package.json</code> to</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">1</span><span class="cl"><span class="s2">&#34;build&#34;</span><span class="err">:</span> <span class="s2">&#34;INLINE_RUNTIME_CHUNK=false craco build&#34;</span></span></span></code></pre></div><h2 id="setup-for-development">Setup for development</h2>
<p>The goal is to create a live-reloading environment without ejecting.</p>
<p>First install the Webpack extension reloader plugin.</p>





<pre tabindex="0"><code>npm install -D webpack-extension-reloader</code></pre><p>Then put the following script in <code>scripts/watch.js</code> and make it executable <code>chmod +x ./scripts/watch.js</code>.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="ch">#!/usr/bin/env node
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="ch"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Based on: https://mmazzarolo.com/blog/2019-10-19-browser-extension-development/
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// Updated for: https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#configuration
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">// Force a &#34;development&#34; environment in watch mode
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">BABEL_ENV</span> <span class="o">=</span> <span class="s2">&#34;development&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> <span class="o">=</span> <span class="s2">&#34;development&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;fs-extra&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="kr">const</span> <span class="nx">paths</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;react-scripts/config/paths&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="kr">const</span> <span class="nx">webpack</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;webpack&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="kr">const</span> <span class="nx">colors</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;colors/safe&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="kr">const</span> <span class="nx">ExtensionReloader</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;webpack-extension-reloader&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1">// use Craco to create the Webpack development config
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">createWebpackDevConfig</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;@craco/craco&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="kr">const</span> <span class="nx">cracoConfig</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;../craco.config.js&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="kr">const</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">createWebpackDevConfig</span><span class="p">(</span><span class="nx">cracoConfig</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1">// The classic webpack-dev-server can&#39;t be used to develop browser extensions,
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1">// so we remove the &#34;webpackHotDevClient&#34; from the config &#34;entry&#34; point.
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span><span class="nx">config</span><span class="p">.</span><span class="nx">entry</span> <span class="o">=</span> <span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">entry</span><span class="p">)</span> <span class="o">?</span> <span class="nx">config</span><span class="p">.</span><span class="nx">entry</span> <span class="o">:</span> <span class="nx">config</span><span class="p">.</span><span class="nx">entry</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">entry</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="k">return</span> <span class="o">!</span><span class="nx">entry</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="s2">&#34;webpackHotDevClient&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1">// Edit the Webpack config by setting the output directory to &#34;./build&#34;.
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span><span class="nx">config</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">path</span> <span class="o">=</span> <span class="nx">paths</span><span class="p">.</span><span class="nx">appBuild</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="nx">paths</span><span class="p">.</span><span class="nx">publicUrl</span> <span class="o">=</span> <span class="nx">paths</span><span class="p">.</span><span class="nx">appBuild</span> <span class="o">+</span> <span class="s2">&#34;/&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="c1">// Add the webpack-extension-reloader plugin to the Webpack config.
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="c1">// It notifies and reloads the extension on code changes.
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="c1"></span><span class="nx">config</span><span class="p">.</span><span class="nx">plugins</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">ExtensionReloader</span><span class="p">());</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1">// Start Webpack in watch mode.
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">compiler</span> <span class="o">=</span> <span class="nx">webpack</span><span class="p">(</span><span class="nx">config</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="kr">const</span> <span class="nx">watcher</span> <span class="o">=</span> <span class="nx">compiler</span><span class="p">.</span><span class="nx">watch</span><span class="p">({},</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="c1">// Every time Webpack finishes recompiling copy all the assets of the
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span>    <span class="c1">// &#34;public&#34; dir in the &#34;build&#34; dir (except for the index.html)
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1"></span>    <span class="nx">fs</span><span class="p">.</span><span class="nx">copySync</span><span class="p">(</span><span class="nx">paths</span><span class="p">.</span><span class="nx">appPublic</span><span class="p">,</span> <span class="nx">paths</span><span class="p">.</span><span class="nx">appBuild</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">      <span class="nx">dereference</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">      <span class="nx">filter</span><span class="o">:</span> <span class="nx">file</span> <span class="p">=&gt;</span> <span class="nx">file</span> <span class="o">!==</span> <span class="nx">paths</span><span class="p">.</span><span class="nx">appHtml</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="c1">// Report on console the succesfull build
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="c1"></span>    <span class="nx">console</span><span class="p">.</span><span class="nx">clear</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="nx">colors</span><span class="p">.</span><span class="nx">green</span><span class="p">(</span><span class="s2">&#34;Compiled successfully!&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="s2">&#34;Built at&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toLocaleTimeString</span><span class="p">());</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">info</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="s2">&#34;Note that the development build is not optimized.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="s2">&#34;To create a production build, use yarn build.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><p>Finally, add a <code>watch</code> script to your <code>package.json</code>.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">1</span><span class="cl"><span class="s2">&#34;watch&#34;</span><span class="err">:</span> <span class="s2">&#34;./scripts/watch.js&#34;</span></span></span></code></pre></div><h2 id="5-start-development">5. Start development</h2>
<p>You can now replace the created example with your html that is styled with Tailwind CSS. To develop your extension with live-reloading just run <code>npm run watch</code> or <code>npm run build</code> for production.</p>
<p>To load an extension in Firefox, open <code>about:debugging</code>, click <code>This Firefox</code> then click <code>Load Temporary Add-on...</code> and open your <code>build/manifest.json</code> file.</p>
<h2 id="references">References</h2>
<p>This blog article is mainly based on Matteo&rsquo;s article <a href="https://mmazzarolo.com/blog/2019-10-19-browser-extension-development/">Developing a browser extension with Create React App</a>. But as we replaced <code>react-scripts</code> with <code>cargo</code>, we need to make a few adjustments here.</p>
<h3 id="resources">Resources</h3>
<ul>
<li><a href="https://mmazzarolo.com/blog/2019-10-19-browser-extension-development/">Developing a browser extension with Create React App</a></li>
<li><a href="https://tailwindcss.com/docs/guides/create-react-app">Install Tailwind CSS with Create React App</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension">Your first extension</a></li>
</ul>
]]></content:encoded></item><item><title>Self serve Fontawesome icons via icon classes with NextJS</title><link>https://thacoon.com/blog/nextjs-react-serve-fontawesome/</link><pubDate>Tue, 15 Dec 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/nextjs-react-serve-fontawesome/</guid><description>Install the free version of Font Awesome You can easily get the latest free version via npm or yarn.
1$ npm install --save @fortawesome/fontawesome-free 2$ yarn add @fortawesome/fontawesome-freeInclude the css file Include the css file from node_modules into your _app.js file.
1import &amp;#39;../node_modules/@fortawesome/fontawesome-free/css/all.min.css&amp;#39;;Use it Now you can use fontawesome via the icon class, e.g. fas fa-dog.
1&amp;lt;i className=&amp;#34;fas fa-dog&amp;#34; /&amp;gt;</description><content:encoded><![CDATA[<h2 id="install-the-free-version-of-font-awesome">Install the free version of Font Awesome</h2>
<p>You can easily get the latest free version via <code>npm</code> or <code>yarn</code>.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ npm install --save @fortawesome/fontawesome-free
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ yarn add @fortawesome/fontawesome-free</span></span></code></pre></div><h2 id="include-the-css-file">Include the css file</h2>
<p>Include the css file from <code>node_modules</code> into your <code>_app.js</code> file.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">import</span> <span class="s1">&#39;../node_modules/@fortawesome/fontawesome-free/css/all.min.css&#39;</span><span class="p">;</span></span></span></code></pre></div><h2 id="use-it">Use it</h2>
<p>Now you can use fontawesome via the icon class, e.g. <code>fas fa-dog</code>.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">&lt;</span><span class="nt">i</span> <span class="na">className</span><span class="o">=</span><span class="s">&#34;fas fa-dog&#34;</span> <span class="p">/&gt;</span></span></span></code></pre></div>]]></content:encoded></item><item><title>GitLab CI: Auto dependency updates</title><link>https://thacoon.com/blog/gitlab-ci-auto-dependency-update/</link><pubDate>Wed, 08 Jul 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/gitlab-ci-auto-dependency-update/</guid><description>The problem Using components with known vulnerabilities is one of the top ten web application security risks of the famous OWASP Top Ten list. It is easy to forget to update your dependencies of your projects if this is a manual process. I am working in a small team (3 people) and we have to manage/maintain and program multiple services. For example some of our services are programmed in Python. All requirements we are using are pinned to a specific version, so every time we build our docker image the same packages are installed.</description><content:encoded><![CDATA[<h2 id="the-problem">The problem</h2>
<p><a href="https://owasp.org/www-project-top-ten/">Using components with known vulnerabilities</a> is one of the top ten web application security risks of the famous OWASP Top Ten list. It is easy to forget to update your dependencies of your projects if this is a manual process. I am working in a small team (3 people) and we have to manage/maintain and program  multiple services. For example some of our services are programmed in Python. All requirements we are using are pinned to a specific version, so every time we build our docker image the same packages are installed. However, if a service is completed or no more work is currently required its pretty easy to forget to update the pinned versions in the requirements file. If this would be the case our services may have known vulnerabilities that were already fixed and the chances of a successful attack would be higher.</p>
<h2 id="the-solution">The solution</h2>
<p>However, this process can be automated using <a href="https://about.gitlab.com/">GitLab</a> and there great CI/CD integration. GitLab&rsquo;s pipelines let you automate steps  like builds, tests, and deployments. GitLab pipelines are normally run based on certain conditions met, e.g. if you push your code. However, you can also schedule pipelines at specific intervals. For example every Monday at 5 a.m. or once a day.</p>
<p>In your projects sidebar navigate to <strong>CI / CD &gt; Schedules</strong>.</p>
<p><img alt="GitLab project sidebar" src="/blog/gitlab-ci-auto-dependency-updates/sidebar-gitlab.png"></p>
<p>Then click on <strong>New schedule</strong> button and fill out the form and save it. I choose the master branch as my target branch and scheduled the pipeline for every Monday at 5 a.m.</p>
<p><img alt="Pipeline schedule form" src="/blog/gitlab-ci-auto-dependency-updates/pipeline-schedule-form.png"></p>
<p>When the scheduled pipeline is run I want to create a merge request if any package could be updated.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .gitlab-ci.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">bot</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="c"># only run if this is a scheduled pipeline</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">only</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span>- <span class="l">schedules</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">python:3.8</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span>- <span class="l">cd $CI_PROJECT_DIR</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="c"># Checkout to a new branch so we push the changes to a new branch</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span>- <span class="l">git checkout -b auto-update-$(date &#39;+%Y-%m-%d&#39;)</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span>- <span class="l">apt-get install -qy curl</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="c"># We are using pip-tools to handle/pin our dependencies</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span>- <span class="l">pip install pip-tools</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span>- <span class="l">chmod 700 update.sh</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="c"># This script updates my requirements file.</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="c"># This can be customized however you want to fit your needs.</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span>- <span class="l">./update.sh</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="c"># If any line was changed then commit the changes, push them and create a merge request</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span>- <span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="sd">      lines=$(git status -s | wc -l)
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="sd">      if [ $lines -gt 0 ];then
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="sd">        echo &#34;committing&#34;
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="sd">        git config --global user.name &#34;${AUTO_COMMITTER_NAME}&#34;
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="sd">        git config --global user.email &#34;${AUTO_COMMITTER_EMAIL}&#34;
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="sd">        git add .
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="sd">        git commit -m &#34;auto-update-$(date &#39;+%Y-%m-%d&#39;)&#34;
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="sd">        git push &#34;https://${GITLAB_USER_NAME}:${GIT_PUSH_TOKEN}@${CI_REPOSITORY_URL#*@}&#34;
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="sd">        BODY=&#34;{\&#34;id\&#34;: \&#34;${CI_PROJECT_ID}\&#34;, \&#34;source_branch\&#34;: \&#34;auto-update-$(date &#39;+%Y-%m-%d&#39;)\&#34;, \&#34;target_branch\&#34;: \&#34;master\&#34;, \&#34;remove_source_branch\&#34;: true, \&#34;title\&#34;: \&#34;Auto Update $(date &#39;+%Y-%m-%d&#39;)\&#34;}&#34;
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="sd">        curl -X POST &#34;https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/merge_requests&#34; \
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="sd">                --header &#34;PRIVATE-TOKEN:${GIT_PUSH_TOKEN}&#34; \
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="sd">                --header &#34;Content-Type: application/json&#34; \
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="sd">                --data &#34;${BODY}&#34;;
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="sd">        echo &#34;Opened a new merge request: \&#34;Auto Update $(date &#39;+%Y-%m-%d&#39;)\&#34; and assigned to you&#34;;
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="sd">        exit;
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="sd">      else
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="sd">        echo &#34;no change, nothing to commit&#34;
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="sd">      fi</span><span class="w">      
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w"></span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">  </span><span class="c"># dont run if this is a scheduled pipeline</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">  </span><span class="nt">except</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">    </span>- <span class="l">schedules</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">  </span><span class="l">...</span></span></span></code></pre></div><p>The <code>update.sh</code> script is responsible to update the requirements file and pin the newest version of each package. We are using <a href="https://github.com/jazzband/pip-tools/">pip-tools</a> to handle/pin our dependencies. It&rsquo;s a great tool. You only need to define you packages you want to install and it automatically pinns the newest version of this packages and the packages dependencies in a requirements.txt file. It makes it super easy to use pinned packages.</p>
<p>For example your <code>update.sh</code> script could be as simple as the following. This will generate up-to date pinned requirements files.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cp"></span><span class="c1"># update.sh</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">pip-compile --upgrade requirements/*.in -o requirements/all.txt
</span></span><span class="line"><span class="ln">4</span><span class="cl">pip-compile --upgrade requirements/base.in requirements/tests.in -o requirements/tests.txt
</span></span><span class="line"><span class="ln">5</span><span class="cl">pip-compile --upgrade requirements/base.in requirements/production.in -o requirements/production.txt</span></span></code></pre></div><p>Finally, we just need to define some environment variables. Go to <strong>Settings -&gt; CI/CD</strong> and add three variables:</p>
<ol>
<li><code>AUTO_COMMITTER_EMAIL</code> which is the email that should be assigned to the commit.</li>
<li><code>AUTO_COMMITTER_NAME</code> which is the name that should be assigned to the commit.</li>
<li><code>GIT_PUSH_TOKEN</code> an api token, needed for authorization to push the commit and open a merge request. To create an token navigate to <strong>User Settings -&gt; Access Tokens</strong> and create a personal access token with api scope. The access token needs to be generated by the person who created the schedule pipeline. You may create a custom bot user, so no one has to use her own access token.</li>
</ol>
<p><img alt="CI Variables" src="/blog/gitlab-ci-auto-dependency-updates/ci-variables.png"></p>
<p>If you want to test it, navigate back to <strong>CI / CD &gt; Schedules</strong> and click the play button to run the pipeline.</p>
<p>Now you have a scheduled pipeline that runs every Monday at 5 a.m., checks whether any dependency can be updated and if so it creates a merge request. This can then be approved by you and merge into the master branch. Make sure you have a good test coverage so the new versions of your dependencies do not break your code.</p>
<p><img alt="CI Variables" src="/blog/gitlab-ci-auto-dependency-updates/pipeline-schedule-list.png"></p>
<p>If you have any further questions, I made a mistake or something else you can contact me. Either write a comment or contact me via email.</p>
]]></content:encoded></item><item><title>Green IT Miniseries Part 1 - Energy consumption</title><link>https://thacoon.com/blog/streaming-co2-costs/</link><pubDate>Thu, 30 Apr 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/streaming-co2-costs/</guid><description>You are right know probably looking on a screen, may it be your smartphone&amp;rsquo;s screen or the one from your labtop. You may even stream a nice song while reading this lines. You probably have used the internet today to stream some nice videos, learn about something new, socialized online or just chilled and played a nice game. But have you ever thought about the backbone of all this. About all the servers or the whole overscaled network that is needed to provide you with all these things we take for granted every day.</description><content:encoded><![CDATA[<p>You are right know probably looking on a screen, may it be your smartphone&rsquo;s screen or the one from your labtop. You may even stream a nice song while reading this lines. You probably have used the internet today to stream some nice videos, learn about something new, socialized online or just chilled and played a nice game. But have you ever thought about the backbone of all this. About all the servers or the whole overscaled network that is needed to provide you with all these things we take for granted every day. Have you ever thought about the implications of this, for example the electricity that is needed to power all of this. And its not just you who is using this global, interconnected, giant infrastructure but billions of users and devices.</p>
<p>So, taking into account the global communication networks, data centers and personal computers you could approximate the needed electricity consumption. Turns out for the year 2012 this was calculated to be about 930 TWh [EINS Consortium et al. 2013]. This correspondence to 4.7% of the total worldwide electricity consumption in 2012 [EINS Consortium et al. 2013]. This translates to about 530 Mt of CO<!-- raw HTML omitted -->2<!-- raw HTML omitted -->, which corresponds to 1.7% of the worldwide total amount of greenhouse gases in 2012 [Gelenbe et al.].</p>
<p>However, this calculation did not include TVs, smart phones, audio devices, manufacturing and dispose of equipment [EINS Consortium et al. 2013]. If you add the electricity consumption for manufacturing, smartphones and TVs the total electricity consumption almost doubles to about 1,815 TWh per year [Corcoran et al.]. This then correspondence to 9,17% of the worldwide electricity consumption and 3.3% of the worldwide greenhouse emissions in 2012.</p>
<p>Before 2012 growing rate of 6.6% per year in the collective electricity consumption of communication networks, data centers and personal computers has been seen [EINS Consortium et al. 2013]. Therefore, the today&rsquo;s electricity consumption and CO<!-- raw HTML omitted -->2<!-- raw HTML omitted --> emissions are probably significantly underestimated by above numbers. Additionally, the current deep learning and IoT hype are not reflected in this numbers as well.</p>
<p>We all use the internet every day. For example me, most of the time, I sit in front of a screen. And almost any of the things I do require a working internet connection.
Today we all use the possibilities of the world wide web to socialize, learn, entertainment purpose, work and heavily rely on it. However, what we often don&rsquo;t see or don&rsquo;t think of are the causalities. The increased usage of information and communication technologies does not come at zero costs. The needed electricity to power and manufacture all these servers, devices and things has grown enormously. But also dependencies on rare materials and violations of human rights and environmental destruction to gather the needed materials.
Facing a climate crisis right know we need to rethink, grow awareness and change it, fast. Therefore, I want to dedicate a mini series on this topic.</p>
<h2 id="references">References</h2>
<p>[Corcoran et al.] Corcoran, P. &amp; Andrae, A. Emerging Trends in Electricity Consumption for Consumer ICT. 57.</p>
<p>[EINS Consortium et al. 2013] EINS Consortium and others. Overview of ICT energy consumption (D8. 1). (2013).</p>
<p>[Gelenbe et al.] Gelenbe, E. &amp; Caseau, Y. The impact of information technology on energy consumption and carbon emissions. Ubiquity 2015, 1–15 (2015).</p>
]]></content:encoded></item><item><title>Chrome vs. AdBlockers - Manifest V3 Summary</title><link>https://thacoon.com/blog/chrome-manifest-v3/</link><pubDate>Tue, 21 Apr 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/chrome-manifest-v3/</guid><description>What&amp;rsquo;s it about? Last year Google announced to work on Manifest V3 (MV3) and a lot of voices raised that Google wants to kill adblockers. This resulted in a back and forth between the Chrome developers and the adblocking community.
MV3 was released as a preview/alpha on October 31st, 2019 and a stable release is planned for 2020.
Currently the Chrome browsers offers extensions the possibility to observe and block network requests via the webRequest API.</description><content:encoded><![CDATA[<h2 id="whats-it-about">What&rsquo;s it about?</h2>
<p>Last year Google announced to work on <a href="https://developer.chrome.com/extensions/migrating_to_manifest_v3">Manifest V3 (MV3)</a> and a lot of voices raised that Google wants to kill adblockers. This resulted in a back and forth between the Chrome developers and the adblocking community.</p>
<p>MV3 was released as a preview/alpha on October 31st, 2019 and a stable release is planned for 2020.</p>
<p>Currently the Chrome browsers offers extensions the possibility to observe and block network requests via the webRequest API. However, the Chrome developers raised concerns about the security and performance impact by this practice. Therefore, the new declarativeNetRequest API was proposed to handle blocking of network requests. With this new API extensions should be able to tell Chrome what to do with a given request. However, the amount of entries are limited. First to 30.000 entries but then was raised to 150.000. As EasyList, a popular blocking list used in all adblockers, already hash 68,000 entries on its own. And some adblockers also uses more advanced techniques than simple blacklisting.[<a href="https://docs.google.com/document/d/1nPu6Wy4LWR66EFLeYInl3NzzhHzc-qnk4w4PX-0XMw8/edit">Manifest V3</a>] [<a href="https://easylist.to/">Easy List</a>]</p>
<h2 id="comments-from-the-adblock-community">Comments from the adblock community</h2>
<blockquote>
<p>Use privacy/performance as Trojan arguments to rationalize reducing user agency over what all bloated web sites throw at people&rsquo;s user agents. That new declarativeNetRequest API seriously reduces what blockers can do, to the point they will become distinguishable only by their UI, not their capabilities. As a user, I personally wouldn&rsquo;t accept browsing the world wild web without the advanced features in uBO, I find this unthinkable. [<a href="https://github.com/uBlockOrigin/uBlock-issues/issues/338">source</a>]</p>
<p>&ndash; <!-- raw HTML omitted -->Raymond Hill (gorhill) (developer of <a href="https://github.com/gorhill/uBlock">uBlock Origin</a> and <a href="https://github.com/gorhill/uMatrix">uMatrix</a>)<!-- raw HTML omitted --></p>
</blockquote>
<blockquote>
<p>There is no way to transpose either either dynamic filtering, dynamic URL filtering, per-site/per-scope switch logic (let&rsquo;s refer to all these as &ldquo;dynamic filtering&rdquo;), into static filters. Dynamic filtering logic requires an arbitrary amount of block/allow rules overriding other block/allow rules based on specificity. There is no concept of specificity in static filtering &ndash; and even more, there is no concept of dynamic filtering rules relinquishing filtering to static filters (dynamic filtering&rsquo;s noop rules).</p>
<p>And there I was recently testing how uBO handled over half a million network filters with the 3rd-gen HNTrie&hellip; [<a href="https://github.com/uBlockOrigin/uBlock-issues/issues/338">source</a>]</p>
<p>&ndash; <!-- raw HTML omitted -->Raymond Hill (gorhill) (developer of <a href="https://github.com/gorhill/uBlock">uBlock Origin</a> and <a href="https://github.com/gorhill/uMatrix">uMatrix</a>)<!-- raw HTML omitted --></p>
</blockquote>
<blockquote>
<p>For the sake of not just the present, but also the healthy and open web ecosystem of the future, we need to make sure that we allow APIs that continue to foster innovation. Our very serious concern is that the proposed changes in manifest V3, especially with the proposed changes to webRequest and the current implementation of declarativeNetRequest might very well do the opposite. [&hellip;] Most modern ad blockers are already quite efficient and are constantly looking to improve performance further. We would encourage the Chromium team to feel free to engage more with the ad blocking developer community regarding manifest V3. We would be happy to provide more technical feedback, suggestions and support. <a href="https://docs.google.com/document/d/1sKZFojq_fUusrebKsyNHfRk_9kpbIALFPS1Nvpddfs8/edit">source</a></p>
<p>&ndash; <!-- raw HTML omitted -->Feedback from various contributors/supporters of adblock community<!-- raw HTML omitted --></p>
</blockquote>
<blockquote>
<p>The new API is not in itself a bad thing, but it becomes a bad thing when it’s the only option because it lacks the flexibility that the Web Requests API provides. <a href="https://www.wired.com/story/google-chrome-ad-blockers-extensions-api/">source</a></p>
<p>&ndash; <!-- raw HTML omitted -->Jeremy Tillman (president of <a href="https://www.ghostery.com/">Ghostery</a>)<!-- raw HTML omitted --></p>
</blockquote>
<blockquote>
<p>I actually don&rsquo;t care about the hard-coded limit on blacklists because I use a whitelist, but I need contextual information which the Declarative API&rsquo;s stated purpose is keeping away from extensions. <a href="https://www.zdnet.com/article/google-promises-to-play-nice-with-ad-blockers-again/">source</a></p>
<p>&ndash; <!-- raw HTML omitted -->Giorgio Maone (developer of <a href="https://noscript.net/">NoScript</a>) <!-- raw HTML omitted --></p>
</blockquote>
<h2 id="googles-arguments">Google&rsquo;s arguments</h2>
<p>Google brings up two major arguments for introducing the declarativeNetRequest API, (1) performance and (2) security.</p>
<h3 id="performance">Performance</h3>
<p>Currently via the webRequest API, Chrome sends all the data in a network request to the listening extensions. These extensions than tells Chrome what to do with the request, like allow it, block it or send it with some modifications. Using the declarativeNetRequest API extensions would register a limited amount of rules that tell Chrome on certain types of requests what to do. As evaluation to allow/disallow a request is now performed by the browser, no serializing, shuttling the inter-process messages to the extensions and waiting for a decision for all request data is needed anymore. [<a href="https://blog.chromium.org/2019/06/web-request-and-declarative-net-request.html">Chromium blog post</a>]</p>
<p>Ghostery has made two studies regarding performance influence by adblockers. The first study <a href="https://whotracks.me/blog/adblockers_performance_study.html">Adblockers Performance Study</a> measured the decision times per request for several popular adblockers like uBlock Origin, Ghostery and AdBlock Plus. The measurements returned a sub-millisecond median decision time per request, what compared to the actual time a request needs to be send/returned to a server, is negligible. Additionally, with the upcoming WebAssembly technology adblockers could reduce decision time even more.</p>
<p>But the second study, <a href="https://www.ghostery.com/trackertax/">The Tracker Tax</a>, is even more interesting. They simply compared the loading time of the top 500 websites with and without blocking trackers. It turns out that the average page load time with all trackers blocked is about 2 times faster than when no trackers are blocked. This is a significant performance increase by using adblockers.</p>
<p>And this makes sense. The time for a request to be serialized, passed to the extensions and then waiting for the decision is already very fast. This all happens on your local machine managed by a highly optimized piece of software, your browser. But then the request needs to be sent/travel to the server, be processed by the server and then sent/travel back to you. This is the part that takes most of the time. Every request that can be avoided significantly lowers the loading time. Modern websites today connect to so many different services, significantly slowing down the page load time. I deeply recommend you to install <a href="https://github.com/gorhill/uMatrix">uMatrix</a> or <a href="https://noscript.net/">NoScript</a> and then visit some of your favorite websites. This would give you an impression of many web services are contacted on each visit.</p>
<h3 id="security">Security</h3>
<p>Google states that 42% of malicious extensions use the Web Request API. As every network request is passed to the extensions a malicious extension may has access to sensitive data.</p>
<p>However,</p>
<blockquote>
<p>The webRequest API is not going to be fully removed as part of Manifest V3. In particular, there are currently no planned changes to the observational capabilities of webRequest (i.e., anything that does not modify the request). [<a href="https://groups.google.com/a/chromium.org/forum/#!topic/chromium-extensions/veJy9uAwS00%5B1-25%5D">source</a>]</p>
<p>&ndash; Simeon Vincent (Google Extensions Developer Advocate)</p>
</blockquote>
<p>So malicious extensions are still able to grab sensitive data but adblockers are limtied in there functionality.</p>
<p>Additionaly, adblockers help preventing being infected by <a href="https://en.wikipedia.org/wiki/Malvertising">Malvertising</a>, a technique to use online advertising to spread malware.</p>
<h2 id="impact-on-electricity-consumption">Impact on electricity consumption</h2>
<p>This may  be a bit off topic, but I found it too interesting to ignore.</p>
<p>Just recently a study, <a href="https://www.mdpi.com/2227-7080/8/2/18">Energy Conservation with Open Source Ad Blockers</a>, was published that evaluated the impact of adblockers regarding electricity consumption. Every request made consumes some amount of electricity, because it needs to be transferred to a destination, being processed and a response sent back. So every request avoided actually reduce the electricity consumption. It was found out that using adblockers has a significantly impact/potential not only on reducing electricity consumption.</p>
<blockquote>
<p><strong>Energy Conservation with Open Source Ad Blockers - Abstract</strong></p>
<p>Internet-related electricity consumption is rising rapidly as global Internet users spend more than 6.5 h per day online. Open source ad blockers have the potential to reduce the time and thus electricity spent using computers by eliminating ads during Internet browsing and video streaming. In this study, three open source ad blockers are tested against a no-ad blocker control. Page load time is recorded for browsing a representative selection of the globally most-accessed websites, and the time spent watching ads on videos is quantified for both trending and non-trending content. The results show that page load time dropped 11% with AdBlock+, 22.2% with Privacy Badger, and 28.5% with uBlock Origin. Thus, uBlock Origin has the potential to save the average global Internet user more than 100 h annually. The energy conserved if everyone in the United States used the open source ad blocker would save over 36 Americans lives per year if it were to offset coal-fired electricity generated-based pollution. In the United States, if all Internet users enabled Privacy Badger on their computers, Americans would save more than $91 million annually. Globally, uBlock Origin could save consumers more than $1.8 billion/year. Open source ad blockers are a potentially effective technology for energy conservation. [<a href="https://www.mdpi.com/2227-7080/8/2/18">source</a>]</p>
<p>&ndash; <!-- raw HTML omitted -->Pearce, J.M. Energy Conservation with Open Source Ad Blockers. Technologies 2020, 8, 18.<!-- raw HTML omitted --></p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>After Chrome announced Manifest V3 a lot of voices raised that Google wants to kill adblockers. However, Google always negated this accuse and has put forward technical arguments to defend there decisions. But as shown the two major arguments performance and security do not even hold true on a basic consideration.</p>
<p>By the way in 2018 over 85% of Alphabets (owner of Google) total revenue was made from ad revenues <a href="https://www.sec.gov/Archives/edgar/data/1652044/000165204419000004/goog10-kq42018.htm#sB8A92C82A7085B27A37F412D2216BC6F">Alphabet Annual Report</a>. Besides all the products Google has created they are still mainly an ad company, it is still there core business model. Therefore, any blocking of ads has an influence on there profit. Adblockers directly affect Google&rsquo;s profit. And even if Google has helped improving the web it is still a company that is responsible for there stackholders and maximizing profit. Therefore, adblockers are a big business risk for Google as Google said by themselve</p>
<blockquote>
<p>New and existing technologies could affect our ability to customize ads and/or could block ads online, which would harm our business.</p>
<p>Technologies have been developed to make customizable ads more difficult or to block the display of ads altogether and some providers of online services have integrated technologies that could potentially impair the core functionality of third-party digital advertising. Most of our Google revenues are derived from fees paid to us in connection with the display of ads online. As a result, such technologies and tools could adversely affect our operating results. [<a href="https://www.sec.gov/Archives/edgar/data/1652044/000165204419000004/goog10-kq42018.htm#sB8A92C82A7085B27A37F412D2216BC6F">source</a>]
&ndash; <!-- raw HTML omitted --><!-- raw HTML omitted --></p>
</blockquote>
<p>Google Chrome now dominates the browser market with <a href="https://www.statista.com/statistics/544400/market-share-of-internet-browsers-desktop/">about 68 percent</a> of the global desktop internet browser market share. Additionally, many browser are based on Chrome as Microsoft&rsquo;s Edge or Opera. This gives Google the power to force new APIs and overall shape the future of the internet as no other company. Not even speaking about there supremacy as a search engine or additional services. But being an ad and stackholder company, Google&rsquo;s vision of the future may not be always the best for us and a free and open web for all of us.</p>
<p>So maybe it&rsquo;s finally time to ditch Chrome&hellip;</p>
]]></content:encoded></item><item><title>Using virtualenv with Jupyter Notebooks</title><link>https://thacoon.com/blog/jupyter-virtualenv-kernel/</link><pubDate>Fri, 13 Mar 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/jupyter-virtualenv-kernel/</guid><description>When using a Jupyter I also want to use virtualenv to keep my different python environments isolated. But I always forget how I do this, so here is how.
Create a new virtual environment or enter an already existing one and install the ipykernel. The following code creates a new one, to enter an existing one use workon my_env.
1$ mkvirtualenv my_venv 2$ pip install ipykernel 3$ python -m ipykernel install --user --name my_venvThen you can start your juypter lab or notebook in another terminal.</description><content:encoded><![CDATA[<p>When using a <a href="https://jupyter.org/">Jupyter</a> I also want to use virtualenv to keep my different python environments isolated. But I always forget how I do this, so here is how.</p>
<p>Create a new virtual environment or enter an already existing one and install the <code>ipykernel</code>. The following code creates a new one, to enter an existing one use <code>workon my_env</code>.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ mkvirtualenv my_venv
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ pip install ipykernel
</span></span><span class="line"><span class="ln">3</span><span class="cl">$ python -m ipykernel install --user --name my_venv</span></span></code></pre></div><p>Then you can start your juypter lab or notebook in another terminal.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ jupyter lab
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ jupyter notebook</span></span></code></pre></div><p>Follow the generated link and choose the kernel called <code>my_env</code>.</p>
<p>If you need to install a python package, e.g. <code>matplotlib</code>, just install it into your virtual environment to be able to use it in jupyter.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ pip install matplotlib</span></span></code></pre></div><h3 id="additional">Additional</h3>
<p>If you are using ArchLinux, <em>btw. it just got 18 years old (initial release was 11 March 2002)</em>, you can install <em>Jupyter Notebook</em> or <em>Jupyter Lab</em> via pacman.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ sudo pacman -S jupyterlab
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ sudo pacman -S jupyter-notebook</span></span></code></pre></div>]]></content:encoded></item><item><title>Named Tuples in Python</title><link>https://thacoon.com/blog/python-named-tuples/</link><pubDate>Sat, 18 Jan 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/python-named-tuples/</guid><description>As I read the great book Python Tricks by Dan Bader I learned about named tuples and was pretty amazed about this. Named tuples can help you to make your code more structured and readable in certain circumstances. They can be used as an alternative to dicts or unstructured tuples. Here is how they work.
1&amp;gt;&amp;gt;&amp;gt; from collections import namedtuple 2&amp;gt;&amp;gt;&amp;gt; Dog = namedtuple(&amp;#39;Dog&amp;#39;, &amp;#39;breed color character&amp;#39;) # same as the following 3&amp;gt;&amp;gt;&amp;gt; Dog = namedtuple(&amp;#39;Dog&amp;#39;, [&amp;#39;breed&amp;#39;, &amp;#39;color&amp;#39;, &amp;#39;character&amp;#39;]) 4&amp;gt;&amp;gt;&amp;gt; rocky = Dog(&amp;#39;pitbull&amp;#39;, &amp;#39;brown&amp;#39;, &amp;#39;excited&amp;#39;) 5&amp;gt;&amp;gt;&amp;gt; rocky.</description><content:encoded><![CDATA[<p>As I read the great book Python Tricks by Dan Bader I learned about named tuples and was pretty amazed about this. Named tuples can help you to make your code more structured and readable in certain circumstances. They can be used as an alternative to dicts or unstructured tuples. Here is how they work.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">Dog</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span><span class="s1">&#39;Dog&#39;</span><span class="p">,</span> <span class="s1">&#39;breed color character&#39;</span><span class="p">)</span> <span class="c1"># same as the following</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">Dog</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span><span class="s1">&#39;Dog&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;breed&#39;</span><span class="p">,</span> <span class="s1">&#39;color&#39;</span><span class="p">,</span> <span class="s1">&#39;character&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">rocky</span> <span class="o">=</span> <span class="n">Dog</span><span class="p">(</span><span class="s1">&#39;pitbull&#39;</span><span class="p">,</span> <span class="s1">&#39;brown&#39;</span><span class="p">,</span> <span class="s1">&#39;excited&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">rocky</span><span class="o">.</span><span class="n">breed</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s1">&#39;pitbull&#39;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">rocky</span><span class="o">.</span><span class="n">color</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s1">&#39;brown&#39;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">rocky</span><span class="o">.</span><span class="n">character</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s1">&#39;excited&#39;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">rocky</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s1">&#39;brown&#39;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">rocky</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">Dog</span><span class="p">(</span><span class="n">breed</span><span class="o">=</span><span class="s1">&#39;pitbull&#39;</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">&#39;brown&#39;</span><span class="p">,</span> <span class="n">character</span><span class="o">=</span><span class="s1">&#39;excited&#39;</span><span class="p">)</span></span></span></code></pre></div><p>The first argument of <em>namedtuple</em> is <em>Dog</em>, this is the type name as <em>namedtuple</em> cannot know the name of the variable. The variables of this class are either defined as a space separated single string or as an array of strings. You can then define a variable as <code>Dog</code> and then access the single variables as you would do it with every other class. Or even using an index as you would expect froma tuple.</p>
<p>If you forgot to assign a variable you get the following error.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sunny</span> <span class="o">=</span> <span class="n">Dog</span><span class="p">(</span><span class="s1">&#39;pitbull&#39;</span><span class="p">,</span> <span class="s1">&#39;brown&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="ne">TypeError</span><span class="p">:</span> <span class="fm">__new__</span><span class="p">()</span> <span class="n">missing</span> <span class="mi">1</span> <span class="n">required</span> <span class="n">positional</span> <span class="n">argument</span><span class="p">:</span> <span class="s1">&#39;character&#39;</span></span></span></code></pre></div><p>As with regular tuples they are immutable.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">rocky</span><span class="o">.</span><span class="n">breed</span> <span class="o">=</span> <span class="s1">&#39;bulldog&#39;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="ne">AttributeError</span><span class="p">:</span> <span class="n">can</span><span class="s1">&#39;t set attribute</span></span></span></code></pre></div><p>You can even add methods as the named tuples are build upon classes.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">DogWithSound</span><span class="p">(</span><span class="n">Dog</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="o">...</span>     <span class="k">def</span> <span class="nf">bark</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="o">...</span>         <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">breed</span> <span class="o">==</span> <span class="s1">&#39;pitbull&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="o">...</span>             <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;WOOF&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="o">...</span>         <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">breed</span> <span class="o">==</span> <span class="s1">&#39;jack russel terrier&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="o">...</span>             <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;woof woof woof&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="o">...</span>         <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="o">...</span>             <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;bark&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">princess</span> <span class="o">=</span> <span class="n">DogWithSound</span><span class="p">(</span><span class="s1">&#39;pitbull&#39;</span><span class="p">,</span> <span class="s1">&#39;brown&#39;</span><span class="p">,</span> <span class="s1">&#39;lovely&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">princess</span><span class="o">.</span><span class="n">bark</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">WOOF</span></span></span></code></pre></div><p>A Python convention is that methods or variables starting with an <em>_</em> underscore should be handled as private. However, this does not count for named tuples. They have a few integrated help methods, starting with an underscore but which belong to the public interface. This was done to prevent naming conflicts with the tuple fields the user defines. In the following a few help methods are shown.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">princess</span><span class="o">.</span><span class="n">_asdict</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">{</span><span class="s1">&#39;breed&#39;</span><span class="p">:</span> <span class="s1">&#39;pitbull&#39;</span><span class="p">,</span> <span class="s1">&#39;color&#39;</span><span class="p">:</span> <span class="s1">&#39;brown&#39;</span><span class="p">,</span> <span class="s1">&#39;character&#39;</span><span class="p">:</span> <span class="s1">&#39;lovely&#39;</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">princess</span><span class="o">.</span><span class="n">_replace</span><span class="p">(</span><span class="n">character</span><span class="o">=</span><span class="s1">&#39;playful&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">DogWithSound</span><span class="p">(</span><span class="n">breed</span><span class="o">=</span><span class="s1">&#39;pitbull&#39;</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">&#39;brown&#39;</span><span class="p">,</span> <span class="n">character</span><span class="o">=</span><span class="s1">&#39;playful&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">Dog</span><span class="o">.</span><span class="n">_make</span><span class="p">([</span><span class="s1">&#39;golden retriever&#39;</span><span class="p">,</span> <span class="s1">&#39;golden&#39;</span><span class="p">,</span> <span class="s1">&#39;happy&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">Dog</span><span class="p">(</span><span class="n">breed</span><span class="o">=</span><span class="s1">&#39;golden retriever&#39;</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">&#39;golden&#39;</span><span class="p">,</span> <span class="n">character</span><span class="o">=</span><span class="s1">&#39;happy&#39;</span><span class="p">)</span></span></span></code></pre></div><p>The method <code>_asdict</code> returns a dict representation of the named tuple. <code>_replace</code> creates flat copy of the tupel and returns the copy with the corresponding field changed. And <code>_make</code> creates a new object based on an iteratable object.</p>
<h1 id="resources">Resources</h1>
<ul>
<li><a href="https://realpython.com/products/python-tricks-book/">Python Tricks by Dan Bader</a></li>
</ul>
]]></content:encoded></item><item><title>Smartphone Apps and OS I Use</title><link>https://thacoon.com/blog/my-smartphone-toolset/</link><pubDate>Wed, 15 Jan 2020 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/my-smartphone-toolset/</guid><description>My Phone Toolset Hardware I am currently using a refurbished Nexus 4 for a few years now. If you think about buying a new phone, please think deeply about buying a refurbished or used phone. We are currently in a climate crisis and cannot afford to be wasteful with our resources. Additionally, it&amp;rsquo;s cheaper and you may end up with a very good deal. Just google refurbished smartphone, there are plenty of suppliers.</description><content:encoded><![CDATA[<h1 id="my-phone-toolset">My Phone Toolset</h1>
<h2 id="hardware">Hardware</h2>
<p>I am currently using a refurbished Nexus 4 for a few years now. If you think about buying a new phone, please think deeply about buying a refurbished or used phone. We are currently in a climate crisis and cannot afford to be wasteful with our resources. Additionally, it&rsquo;s cheaper and you may end up with a very good deal. Just google <a href="https://lmgtfy.com/?q=refurbished+smartphone&s=t">refurbished smartphone</a>, there are plenty of suppliers. Or if you absolutely want to buy a new phone, you could go for the <a href="https://www.fairphone.com/">Fairphone</a>. There phone has a 10/10 iFixit Score, you can easily replace the battery, camera, display and more. Additionally, they a very transparent about their manufacturing process and the materials they use.</p>
<p>Additionally, if you want to install a custom ROM you need to unlock the bootloader, some brands or companies make it easier for you than others. Unlocking the bootloader on Nexus phones is fairly easy. An unlocking step-by-step guide for the Fairphone will be published in the coming weeks.</p>
<h2 id="operating-system">Operating System</h2>
<h3 id="lineage-os">Lineage OS</h3>
<p>LineageOS is a free and open-source operating system based on Android. It&rsquo;s the successor of the custom ROM CyanogenMod. What I like about it is that it does not come with any Crapware. You can use it without any Google related services. However, I myself installed a minimal set of Google servcies, the <a href="https://github.com/opengapps/opengapps/wiki/Pico-Package">pico package of Open GApps</a>. This includes only the Google Play Store, Google Calendar Sync, Dialer Framework, Google Package Installer and Google Play Services. Mainly because the Google Play Store can install app updates automatically in the background. Also some apps will warn you about missing Google Play Services and some may not even work without this service.</p>
<p>There are alternative app stores like <a href="https://f-droid.org/en/packages/com.aurora.store/">Aurora Store</a> that you can install via <a href="https://f-droid.org/en/">F-Droid</a>, an installable catalogue of FOSS (Free and Open Source Software) applications. Aurora allows you to install apps from Google Play Store, even without any Google account. Some apps relay on Google Play Services and warns you about this, but in my experience they work nonetheless. If you do not use your own Google account with Aurora some apps may cannot be installed. As the default accounts are located in Russia and some apps liked LinkedIn are blocked in Russia. Also sometimes the Aurora Store does not work. But if you do not want any Google Service installed or a Google account but still want apps from Google Store, this is your way to go. Additionally, you can download apps from <a href="https://www.apkmirror.com/">APKMirror</a>.</p>
<p>It&rsquo;s available for a good number of devices. See the <a href="https://wiki.lineageos.org/devices/">devices list here</a>. For every device there is also an installation instruction guide. These guides show you each step that needs to be done to install the custom ROM on your phone. Alternative you can use <a href="https://thacoon.gitlab.io/articles/2017-04/install-custom-android-rom.html">my guide here</a>. Additionally, if you want to install Google services as well you need to install them separately <a href="https://wiki.lineageos.org/gapps.html">Google apps</a>. But this optional step is also pointed out in the installation instructions.</p>
<p>After installing LineageOS I did not root my phone. Because I think it is a security risk to use an admin account all the time. If you get infected by malware you are compromised with a malware that has root access.</p>
<h3 id="copperheados">CopperheadOS</h3>
<p>An alternative Android based OS that focus on security and privacy is <a href="https://copperhead.co/android/">CopperheadOS</a>. It is developed my a Canadian team of security researchers, forensic analysts, and software developers. However, I have not tested it yet, as I did not have any phone supporting it. See what phones are supported on the <a href="https://copperhead.co/android/docs/devices">devices list</a>.</p>
<h2 id="apps">Apps</h2>
<h3 id="app-store">App Store</h3>
<p>Besides Google Play Store I use <a href="https://f-droid.org/de/">F-Droid</a>. On there website it says <em>F-Droid is an installable catalogue of FOSS (Free and Open Source Software) applications for the Android platform. The client makes it easy to browse, install, and keep track of updates on your device</em>. If the app is available in F-Droid catalogue I install it with F-Droid otherwise with Google Play Store.</p>
<h3 id="calendar">Calendar</h3>
<p>Instead of the Google Calendar I use <a href="https://www.simplemobiletools.com/">Simple Calendar Pro</a> and <a href="https://www.davx5.com/">DAVx5</a> to sync it with my email provider. Both apps can be installed with F-Droid. DAVx5 is used to sync the calendar of my email account with my local calendar on my phone.</p>
<p>As an email provider I use <a href="https://posteo.de/en">Posteo</a>. It&rsquo;s a german based company focusing on privacy and sustainability. It costs 1 euro per month, but therefore it is ad-free, privacy friendly and the servers are running with 100% electricity from Greenpeace energy. I am using it for years now and it is my absolute favorite email provider.</p>
<h3 id="messengers">Messengers</h3>
<p>As a messenger I mainly use <a href="https://threema.ch/de/">Threema</a> which focus on security and privacy. You can either buy it from the Google Play Store or buy a license in there own shop. If buying a license in there shop you can download the apk file from there website and the app can update itself with you permission once installed. What I like most about it besides having a security and privacy first policy is that the messenger is not connected to you phone number, you could use it even without a SIM card. End-to-end encryption is a very good thing. However, metadaten can still be collected about you and may reveal more about you than you think. Check out this <a href="https://www.bitsoffreedom.nl/2014/07/30/how-your-innocent-smartphone-passes-on-almost-your-entire-life-to-the-secret-service/">article</a>. There is also a <a href="https://netzpolitik.org/2014/metadaten-wie-dein-unschuldiges-smartphone-fast-dein-ganzes-leben-an-den-geheimdienst-uebermittelt/">German version</a> available on Netzpolitik.</p>
<h3 id="finances">Finances</h3>
<p>For tracking my expenses I use the open source app <a href="http://www.myexpenses.mobi/en/">My Expenses</a>. I am really impressed by this, it is actively developed, is highly customizable but is very easy to use.</p>
<h3 id="2-factor-auth">2-Factor-Auth</h3>
<p>As my two-factor authentication app I use <a href="https://github.com/andOTP/andOTP">andOTP</a>. It implements Time-based One-time Passwords (TOTP) and HMAC-Based One-Time Passwords (HOTP). But remember to make backups and store them additionally somewhere else. One time I forgot to make a backup before resetting my phone and therefore lost one of my keys. Since then I always have a backup of these laying somewhere on my disks.</p>
<h3 id="browser">Browser</h3>
<p>As a browser I use the <a href="https://guardianproject.info/apps/org.torproject.torbrowser/">Tor Browser</a>. The connection speed is sufficient and I never had any difficulties using it. The Tor Browser replaces Orfox, with which I previously had some difficulties with. If suggest you to try out the new Tor Browser app. Also by increasing the number of people using Tor we can make it more difficult for intelligence agencies and governments to track down human right activists, whistleblowers and persecuted  minorities.</p>
<h3 id="podcasts">Podcasts</h3>
<p>I lately became a very big fan of podcast. As you can consume them during boring tasks like traveling to university/work, housework or something else. And depending on the podcast you hear, you may even learn something new. For managing all of my podcasts I use <a href="https://antennapod.org/">AntennaPod</a>. It allows me to search for podcast from various sources, notifies me when new episodes are online, I can download them and put them in a queue, waiting for me to be listened to.</p>
<h3 id="weather-and-emergency">Weather and Emergency</h3>
<p>If you live in Germany you can install <a href="https://www.bbk.bund.de/DE/NINA/Warn-App_NINA.html">Warn-App NINA</a> and <a href="https://www.dwd.de/DE/leistungen/warnwetterapp/warnwetterapp.html">WarnWetter-App</a>. You can let these app track your personal location or set different areas. If there is a extreame weather or floodwaters or another emergency these apps will send you a notification and inform/warn you about the circumstances.</p>
<h2 id="have-a-i-classfa-fa-heart-heart-icon-stylecolorf83e4b---donate-for-free-open-source-software">Have a <!-- raw HTML omitted --> - Donate for free Open Source Software</h2>
<ul>
<li>If you enjoy/permanently using a open-source software please consider to make a donation to support the further development.</li>
</ul>
]]></content:encoded></item><item><title>Arch Linux encrypted installation</title><link>https://thacoon.com/blog/arch-linux-uefi-encrypted-installation/</link><pubDate>Mon, 12 Aug 2019 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/arch-linux-uefi-encrypted-installation/</guid><description>Download Arch Linux Download the archiso image from https://www.archlinux.org/ Verify your iso. Download the PGP signature. Check if your downloaded image has a good signature $ gpg --keyserver-options auto-key-retrieve --verify archlinux-&amp;lt;version&amp;gt;-dual.iso.sig Copy the iso to an USB stick Insert your USB stick or SD card and check with $ lsblk to which device it is mapped. You probably recognize your stick from the specific size it has. If mounted umount the mounted partitions it, replace /dev/sdXY with the correct path (X) and correct partition number (Y).</description><content:encoded><![CDATA[<h2 id="download-arch-linux">Download Arch Linux</h2>
<ol>
<li>Download the archiso image from <a href="https://www.archlinux.org/">https://www.archlinux.org/</a></li>
<li>Verify your iso.
<ol>
<li>Download the <a href="https://www.archlinux.org/download/">PGP signature</a>.</li>
<li>Check if your downloaded image has a good signature
<ol>
<li><code>$ gpg --keyserver-options auto-key-retrieve --verify archlinux-&lt;version&gt;-dual.iso.sig</code></li>
</ol>
</li>
</ol>
</li>
<li>Copy the iso to an USB stick
<ol>
<li>Insert your USB stick or SD card and check with <code>$ lsblk</code> to which device it is mapped. You probably recognize your stick from the specific size it has.</li>
<li>If mounted umount the mounted partitions it, replace <em>/dev/sdXY</em> with the correct path (<em>X</em>) and correct partition number (<em>Y</em>).
<ol>
<li><code>$ sudo umount -R /dev/sdXY</code></li>
</ol>
</li>
<li>Convert and copy the downloaded iso to your stick. Replace <em>/dev/sdX</em> with the correct path.
<ol>
<li><code>$ sudo dd if=archlinux.iso of=/dev/sdX &amp;&amp; sync # on linux</code></li>
</ol>
</li>
</ol>
</li>
<li>Now you have bootable Arch Linux USB stick or SD card.
5. If it does not boot, make sure secure boot is disabled in BIOS and your SD card is your first boot device.</li>
</ol>
<h2 id="installation-of-base-system">Installation of base system</h2>
<p>Follow my gist <a href="https://gist.github.com/thacoon/05d5a39606ab554455d6713e8a714b2c">arch-linux-uefi-encrypted-install</a>. All steps to install a basic Arch Linux system on your PC is explained there. I try to keep it up-to date.</p>
<h2 id="installation-of-gui">Installation of GUI</h2>
<p>The basic Arch Linux installation does not come with a GUI. How to install it, you can use my other gist <a href="https://gist.github.com/thacoon/96e66f5d475a059cc6d66b61c6366b7a">arch-linux-gnome-install</a> which explains how to install a minimal Gnome with some frequently used programs. Gnome has become my favorite desktop environment over time.</p>
<h1 id="links">Links</h1>
<ul>
<li><a href="https://gist.github.com/thacoon/96e66f5d475a059cc6d66b61c6366b7a">arch-linux-gnome-install</a></li>
<li><a href="https://gist.github.com/thacoon/05d5a39606ab554455d6713e8a714b2c">arch-linux-uefi-encrypted-install</a></li>
</ul>
]]></content:encoded></item><item><title>Arch Linux install hardened kernel</title><link>https://thacoon.com/blog/arch-linux-hardened-kernel/</link><pubDate>Sun, 16 Jul 2017 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/arch-linux-hardened-kernel/</guid><description>Why? Hardening is a process that reduces the surface of vulnerability. It does not change the access controls or fixes any specific bugs but by hardening the kernel many forms of attacks can be prevented. Hardening can be achieved by disabling unnecessary services and daemons, closing all unnecessary ports, using more restrictive system rules and so on. The default hardened linux kernel for Arch uses more security-focused compile-time configuration options than the default linux kernel.</description><content:encoded><![CDATA[<h2 id="why">Why?</h2>
<p>Hardening is a process that reduces the surface of vulnerability. It does not change the access controls or fixes any specific bugs but by hardening the kernel many forms of attacks can be prevented. Hardening can be achieved by disabling unnecessary services and daemons, closing all unnecessary ports, using more restrictive system rules and so on. The default hardened linux kernel for Arch uses more security-focused compile-time configuration options than the default linux kernel. For example it forbids access to kernel logs for certain users, the kernel has the ability to hide other users processes, uses an improved implementation of Address Space layout Randomization for userspace processes, &hellip;</p>
<p>And it is really simple to install.</p>
<h2 id="6-5-4---hardened-kernel">6, 5, 4, &hellip;  hardened kernel</h2>
<ol>
<li>
<p>Install the hardened kernel and hardened kernel headers</p>
<p><code>$ sudo pacman -S linux-hardened linux-hardened-headers</code></p>
</li>
<li>
<p>Check if <em>initramfs-linux-hardened.img</em> and <em>initramfs-linux-hardened-fallback.img</em> exists.</p>
<p><code>$ ls -lsha /boot</code></p>
</li>
<li>
<p>Recreate the ramdisk environment with the hardened kernel selected.</p>
<p><code>$ mkinitcpio -p linux-hardened</code></p>
</li>
<li>
<p>Update the grub config.</p>
<p><code>$ grub-mkconfig -o /boot/grub/grub.cfg</code></p>
</li>
<li>
<p>Optionally: Remove the default kernel. It is not needed as grub should automatically load the hardened kernel and you can choose between the default and the hardened kernel while booting.</p>
<p><code>$ pacman -R linux linux-headers</code></p>
</li>
<li>
<p>Reboot</p>
</li>
</ol>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3 id="virtualbox">Virtualbox</h3>
<p>The <em>virtualbox-host-modules-arch</em> are only compatible with the default linux kernel, but you can install the dkms version. First remove virtualbox <code>$ pacman -R virtualbox virtualbox-host-modules-arch</code> and then reinstall it but this time the dkms version <code>$ pacman -S virtualbox virtualbox-host-dkms</code>.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://wiki.archlinux.org/index.php/security#Kernel_hardening">ArchWiki - Kernel hardening</a></li>
</ul>
]]></content:encoded></item><item><title>Install a custom Android ROM</title><link>https://thacoon.com/blog/install-custom-android-rom/</link><pubDate>Sun, 16 Apr 2017 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/install-custom-android-rom/</guid><description>Some important facts Please read the whole article before doing anything. Your battery charge should be at least &amp;gt;50% before starting the installation. You should know how to use a terminal in Linux. Every device may need some tweaks and startpaging aka. googeling{:target=&amp;quot;_blank&amp;quot;}. If you find any mistakes in this article please send me an email. Use at your own risk We cannot be blamed in case you brick the phone nor in case you lose any important data file in the process.</description><content:encoded><![CDATA[<h2 id="some-important-facts">Some important facts</h2>
<ul>
<li>Please read the whole article before doing anything.</li>
<li>Your battery charge should be at least &gt;50% before starting the installation.</li>
<li>You should know how to use a terminal in Linux.</li>
<li>Every device may need some tweaks and <a href="https://www.startpage.com/">startpaging aka. googeling</a>{:target=&quot;_blank&quot;}.</li>
<li>If you find any mistakes in this article please send me an email.</li>
</ul>
<h2 id="use-at-your-own-risk">Use at your own risk</h2>
<p>We cannot be blamed in case you brick the phone nor in case you lose any important data file in the process.</p>
<h2 id="install-adb">Install adb</h2>
<ul>
<li>Ubuntu/ Kali Linux:
<ul>
<li><code>$ sudo apt-get install adb</code></li>
<li><code>$ sudo apt-get install fastboot</code></li>
</ul>
</li>
<li>Arch Linux
<ul>
<li><code>$ sudo pacman -Syy android-tools</code></li>
</ul>
</li>
</ul>
<h2 id="other-tools">Other tools</h2>
<ul>
<li>If you have a Samsung device you also need to install <a href="http://glassechidna.com.au/heimdall/">Heimdall</a>{:target=&quot;_blank&quot;}
<ul>
<li>Ubuntu/ Kali Linux
<ul>
<li><code>$ sudo apt-get install heimdall-flash</code></li>
</ul>
</li>
<li>Arch Linux
<ul>
<li><code>$ sudo pacman -Syy heimdall</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="backup">Backup</h2>
<ul>
<li>Backup your data before doing anything. If you want to use the terminal to backup your photos etc. you can use adb:
<ul>
<li>First you need to allow USB Debugging. To do this go to <em>Settings -&gt; About</em> and scroll down to <em>Build number</em> and press it several times until you become developer. Then go to <em>Settings -&gt; Developer options</em> and enable <em>USB debugging</em> (<em>Android debugging</em>).</li>
<li>Now you can connect your phone via USB cable to your computer. If you run <code>$ adb devices</code> you should see that one device is connected. Then run <code>$ adb shell</code> to get a shell on you Android phone and look for files you want to backup. You can exit the shell with <code>$ exit</code>. In your computer terminal you can run <code>$ adb pull &lt;remote&gt; &lt;local&gt;</code> to copy files from your phone to your local computer.
<ul>
<li>Your probably find your files in <em>/storage/self/primary</em></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="download-lineageos-or-another-custom-android-rom">Download LineageOS or another custom Android ROM</h2>
<ul>
<li>Take a look at <em>Settings -&gt; About -&gt; Device model</em> to see what ROM you have to download.
<ul>
<li><a href="https://download.lineageos.org/">LineageOS</a>{:target=&quot;_blank&quot;}
<ul>
<li><a href="https://wiki.lineageos.org/verifying-builds.html">LineageOS - Verifying Build Authenticity</a>{:target=&quot;_blank&quot;}</li>
</ul>
</li>
<li><a href="https://copperhead.co/android/downloads">CopperheadOS</a>{:target=&quot;_blank&quot;}
<ul>
<li><a href="https://copperhead.co/android/docs/install">CopperheadOS - Verifying Build Authenticity</a>{:target=&quot;_blank&quot;}</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="install-custom-recovery-without-root">Install Custom Recovery without root</h2>
<ul>
<li>Download the specific custom recovery image for your device at <a href="https://twrp.me/devices/">TWRP</a>{:target=&quot;_blank&quot;}
<ul>
<li>To verify the image take a look at their <a href="https://twrp.me/faq/pgpkeys.html">FAQ</a>{:target=&quot;_blank&quot;}</li>
</ul>
</li>
<li>Boot in bootloader mode by running <code>$ adb reboot bootloader</code>
<ul>
<li>alternative you can power off your device and press a device specific key combo to start the bootloader
<ul>
<li>for Nexus devices this should be: <em>volume down + power button</em></li>
<li>for Samsung devices this should be: <em>volume down + home button + power button</em></li>
</ul>
</li>
</ul>
</li>
<li>Flash the custom recovery:
<ul>
<li>Non Samsung:
<ul>
<li>First unlock the bootloader with <code>$ sudo fastboot oem unlock</code> and then reboot it. You have to re-enable USB debugging.</li>
<li>Then flash the revocery image with<code>$ sudo fastboot flash recovery twrp.img</code></li>
</ul>
</li>
<li>Samsung: <code>$ heimdall flash --RECOVERY twrp.img --no-reboot</code></li>
</ul>
</li>
<li>Now power off your device and then boot in recovery mode. Again you need to press a device specific key combo to enter custom recovery.
<ul>
<li>for Nexus devices this should be: <em>volume up + power button</em>, alternative you can choose in the bootloader mode to restart in recovery mode.</li>
<li>for Samsung devices this should be: <em>volume up + home button + power button</em>.</li>
</ul>
</li>
<li>Many devices will remove custom recovery if the system is booted and not directly custom recovery. You may have to reinstall the custom recovery. If you have a custom ROM installed, custom recovery won&rsquo;t be removed anymore.</li>
</ul>
<h2 id="install-custom-rom---option-1">Install custom ROM - Option 1</h2>
<ul>
<li>All steps below are done while being in custom recovery.</li>
<li>First we need to push the zip file, we have downloaded a few steps earlier, to our phone. We use adb to do it. With <code>$ adb push &lt;local&gt; &lt;remote&gt;</code> you can upload it to your USB connected phone. Upload it to your internal storage by using:
<ul>
<li><code>$ adb push ROM.zip /sdcard</code></li>
</ul>
</li>
<li>Select <em>Wipe</em> and confirm the factory reset process, alternative you can also wipe your internal storage by using <em>Advanced Wipe</em>.</li>
<li>Select <em>install</em> and choose the custom ROM, the zip file we have just uploaded and confirm the installation process.</li>
<li>Reboot system. TWRP may ask you to install itself as an app but you can deny it.</li>
</ul>
<h2 id="install-custom-rom---option-2">Install custom ROM - Option 2</h2>
<ul>
<li>This way is a bit faster.</li>
<li>While being in custom recovery go to <em>Advanced -&gt; ADB Sideload</em>. Check <em>Wipe Dalvik Cache</em>, <em>Wipe Cache</em> and then swipe to start the sideload. Then go to your computer and run <code>$ adb sideload &lt;local&gt;</code> with <code>&lt;local&gt;</code> being the custom ROM zip file.</li>
<li>Wait until TWRP says it is finished. The percentage on the computer terminal may be inacurrate. There may be a message that no ROM was installed but you can ignore it, the ROM should have been installed.</li>
<li>Reboot system. TWRP may ask you to install itself as an app but you can deny it.</li>
</ul>
<h2 id="updates">Updates</h2>
<p>To update your phone you can follow the same steps. Download the new ROM.zip and use <em>Option 2</em> to sideload your new ROM to your phone. I recommend doing this way because I had some problems with updating LineageOS with the <em>Updater</em> that can be found under <em>Settings -&gt; About phone -&gt; LineageOS updates</em>.</p>
<h2 id="useful-adb-commands">Useful adb commands</h2>
<ul>
<li><em>Settings -&gt; Developer Options -&gt; Enable USB Debugging</em></li>
<li>Useful commands:</li>
</ul>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ adb devices
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ adb connect &lt;host&gt;<span class="o">[</span>:&lt;port&gt;<span class="o">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">$ adb push &lt;local&gt; &lt;remote&gt;
</span></span><span class="line"><span class="ln">4</span><span class="cl">$ adb pull &lt;remote&gt; &lt;local&gt;
</span></span><span class="line"><span class="ln">5</span><span class="cl">$ adb shell</span></span></code></pre></div><h2 id="encrypt-your-phone">Encrypt your phone</h2>
<ul>
<li>In Android the encryption password is tied to your password you use to unlock your screen. This hampers us to use a good encryption password. But you can choose a simple pin to unlock your screen and change your encryption password to a much better one using adb.</li>
<li>Enable <em>USB Debugging</em> and <em>Root access via adb</em> in developer options. Connect your phone to your computer. We need to enter the root shell by first running <code>$ adb root</code> and then <code>$ adb shell</code>. But first you need to allow root access via adb shell by checking <em>Settings -&gt; Developer options -&gt; Root access -&gt; ADB only</em>.</li>
<li>In the adb shell you can now change your encryption password without changing you pin to unlock your screen with the following command:
<ul>
<li><code># vdc cryptfs changepw password &quot;currentpasswd&quot; &quot;newpasswd&quot;</code></li>
</ul>
</li>
<li>And you can verify the password with:
<ul>
<li><code># vdc cryptfs verifypw &quot;yourpass&quot;</code></li>
<li>If you have entered the correct password there should be a <code>0</code> at the end of the output otherwise <code>1</code>.</li>
</ul>
</li>
</ul>
<h2 id="troubleshooting">Troubleshooting</h2>
<ul>
<li>adb has no permissions when trying to sideload a new ROM version





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ adb devices
</span></span><span class="line"><span class="ln">2</span><span class="cl">List of devices attached
</span></span><span class="line"><span class="ln">3</span><span class="cl">02c438ce0ede8404	no permissions<span class="p">;</span> see <span class="o">[</span>http://developer.android.com/tools/device.html<span class="o">]</span></span></span></code></pre></div><ul>
<li>Shutdown the adb server with <code>$ sudo adb kill-server</code> and then restart it with <code>$ sudo adb start-server</code>.</li>
<li>Also enable USB debbuging as shown in the <em>Backup</em> section.
<ul>
<li><em>Settings -&gt; Developer options</em> and enable <em>USB debugging</em> (<em>Android debugging</em>)</li>
<li>Attach your phone to your computer and connect to it with <code>$ sudo adb shell</code>. A window should pop up on your phone, allow the connection and exit the shell.</li>
<li>Now you can reboot in recovery mode and sideload your new ROM version.</li>
</ul>
</li>
</ul>
</li>
<li>fastboot has no permissions
<ul>
<li>If <code>$ fastboot devices</code> returns with an <em>no permissions</em> message try to run fastboot as root.</li>
</ul>
</li>
</ul>
<h2 id="have-a-pink_heart---donate-for-free-open-source-software">Have a &#x1fa77; - Donate for free Open Source Software</h2>
<ul>
<li>If you appreciate their work and would like to support future development please make a donation for the software you use.
<ul>
<li><a href="https://lineageos.org/">LineageOS</a>{:target=&quot;_blank&quot;}</li>
<li><a href="https://twrp.me/">TWRP</a>{:target=&quot;_blank&quot;}</li>
<li><a href="https://copperhead.co/android/donate">CopperheadOS</a>{:target=&quot;_blank&quot;}</li>
<li><a href="http://glassechidna.com.au/donate/">Heimdall</a>{:target=&quot;_blank&quot;}</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title>Install and configure DNSCrypt on Arch Linux</title><link>https://thacoon.com/blog/dnscrypt/</link><pubDate>Sun, 02 Apr 2017 00:00:00 +0000</pubDate><author>thacoon@posteo.de (thacoon)</author><guid>https://thacoon.com/blog/dnscrypt/</guid><description>DNSCrypt DNS stands for Domain Name System. It is a decentralized system that translates domain names to IP addresses that are needed by your computer to connect to other computers. The problem is that even if you use https these queries are unencrypted. This is vulnerable to spoofing and eavesdropping. A malicious user can record your queries and/or change the responses. This opens the door to several vulnerabilities. An attacker can respond with a fake IP address that your browser will try to visit or it act as a silent forward while capturing all what it needs.</description><content:encoded><![CDATA[<h2 id="dnscrypt">DNSCrypt</h2>
<p>DNS stands for Domain Name System. It is a decentralized system that translates domain names to IP addresses that are needed by your computer to connect to other computers. The problem is that even if you use https these queries are unencrypted. This is vulnerable to spoofing and eavesdropping. A malicious user can record your queries and/or change the responses. This opens the door to several vulnerabilities. An attacker can respond with a fake IP address that your browser will try to visit or it act as a silent forward while capturing all what it needs.
DNSCrypt is a network protocol that encrypt DNS queries and responses between you and the DNS resolver. This way spoofing of your DNS queries and eavesdropping can be prevented.</p>
<h2 id="install-and-configure-dnscrypt-on-arch-linux">Install and configure DNSCrypt on Arch Linux</h2>
<ol>
<li>
<p>Install the dnscrypt package with <code>$ sudo pacman -S dnscrypt-proxy</code>.</p>
</li>
<li>
<p>To prevent NetworkManager to change the <em>resolv.conf</em> file we need to edit the <em>NetworkManager.conf</em> file. Go into the conf file with <code>$ sudo nano /etc/NetworkManager/NetworkManager.conf</code> and then add/edit the following in the <code>[main]</code> section.</p>
</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">dns</span><span class="o">=</span>none</span></span></code></pre></div><p>Alternatively you can prevent it from being modified by anythin. To do this we can set a write-protection attribute with <code>$ sudo chattr +i /etc/resolv.conf</code>.</p>
<ol start="3">
<li>Replace the current set of resolver addresses in <code>$ sudo nano /etc/resolv.conf</code> with the following one:</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">nameserver 127.0.0.1</span></span></code></pre></div><ol start="4">
<li>
<p>Choose a resolver address near to your location. A list of resolver addresses can be found <a href="https://github.com/jedisct1/dnscrypt-proxy/blob/master/dnscrypt-resolvers.csv">here</a>. In our case we are using <code>cs-de3</code>. This is a DNS server owned by cryptostorm.is, a VPN service provider.
And then edit the file <code>$ sudo nano /etc/dnscrypt-proxy.conf</code> and change to:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ResolverName cs-de3</span></span></code></pre></div></li>
<li>
<p>To prevent dnscrypt to be run by root, we first need to add another user:
<code>$ sudo useradd -r -d /var/dnscrypt -m -s /sbin/nologin dnscrypt</code></p>
<p>Now change the systemd config file so dnscrypt is started by the added user. Edit the file with <code>$ sudo nano /usr/lib/systemd/system/dnscrypt-proxy.service</code> and add two lines to Service:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="o">[</span>Service<span class="o">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">User</span><span class="o">=</span>dnscrypt
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nv">CapabilityBoundingSet</span><span class="o">=</span>CAP_NET_BIND_SERVICE</span></span></code></pre></div></li>
<li>
<p>No we can start dnscrypt. At first we reload systemctl because we just changed a systemd config file and the we start dnscrypt.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ sudo systemctl daemon-reload
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ sudo systemctl start dnscrypt-proxy.service</span></span></code></pre></div><p>To check if dnscrypt is really executed by a different user than root, we check can it with <code>$ ps -u dnscrypt</code>. You should see something like:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">PID TTY          TIME CMD
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="m">32639</span> ?        00:00:00 dnscrypt-proxy</span></span></code></pre></div><p>If everything is correct you can enable dnscrypt so it starts at boot time. <code>$ sudo systemctl enable dnscrypt-proxy.service</code></p>
</li>
<li>
<p>When you now run an extended test at <a href="https://www.dnsleaktest.com/">DNS leak test</a> you should only see the hostname of the resolver you have just added. In our case something like <code>warlock.deepdns.cryptostorm.net</code>.</p>
</li>
</ol>
]]></content:encoded></item></channel></rss>