<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://blog.fazibear.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.fazibear.me/" rel="alternate" type="text/html" /><updated>2025-07-10T07:17:24+00:00</updated><id>https://blog.fazibear.me/feed.xml</id><title type="html">Programming Adventures with Michał Kalbarczyk</title><subtitle>Programming Adventures with Michał Kalbarczyk</subtitle><author><name>Michał Kalbarczyk</name></author><entry><title type="html">My Own GeekMagic SmallTv</title><link href="https://blog.fazibear.me/my-own-geekmagic-smalltv/" rel="alternate" type="text/html" title="My Own GeekMagic SmallTv" /><published>2024-09-09T00:00:00+00:00</published><updated>2024-09-09T00:00:00+00:00</updated><id>https://blog.fazibear.me/my-own-geekmagic-smalltv</id><content type="html" xml:base="https://blog.fazibear.me/my-own-geekmagic-smalltv/"><![CDATA[<p>Got one! And you know what? Firmware is not open source. I need to write my own right?</p>

<p>RIGHT?</p>

<p>No more talking let’s start.</p>

<p>There are two versions. PRO version with a touch button on top with ESP32 inside, and other one without. The other one have ESP8266 inside.</p>

<p>It’s from aliexpress, you never know what you will get ;)</p>

<p>Wanna look inside? Someone did a video.
Here you are:</p>

<iframe src="https://www.youtube.com/embed/70CDu9o2_Yo" frameborder="0" allowfullscreen=""></iframe>

<p>And this is mine:</p>

<p><img src="/images/minitv.jpg" alt="minitv" /></p>

<p>What to we need?</p>
<ul>
  <li>USB to TTL converter</li>
  <li>few spare pins</li>
  <li>solder iron</li>
  <li>few cables</li>
  <li>computer ;)</li>
</ul>

<p>I think you guess it, but:</p>

<ul>
  <li>GRN to ground pin</li>
  <li>RX to TTLs TX pin</li>
  <li>TX to TTLs RX pin</li>
  <li>VCC to 3.3V (5V works fine ;)</li>
  <li>FLASH - short to ground during power on to upload flash.</li>
</ul>

<p>Now you need to setup PlatformIO new project. If you really want Arduino IDE will be fine.</p>

<p>Create <code class="language-plaintext highlighter-rouge">src/main.c</code> …</p>

<p>And now what? Now we need some informations…</p>

<ul>
  <li>What microcontroller we have</li>
  <li>What tft driver we need to use</li>
  <li>Where the screen is connected?</li>
</ul>

<p>What pins goes to tft socket?</p>

<p>LCD use those pins:</p>
<ul>
  <li>14</li>
  <li>13</li>
  <li>0</li>
  <li>2</li>
  <li>5</li>
</ul>

<p>It’s not enough, uncle google your friend… (ugh! forget to ask chat GPT, next time)</p>

<p>Anyway I found this: https://community.home-assistant.io/t/installing-esphome-on-geekmagic-smart-weather-clock-smalltv-pro/618029</p>

<p>There is lot of informations.
If you carefully read whole conversation you will know that you need to use ST7789 tft driver and pins are:</p>
<ul>
  <li>TFT_CLK 14</li>
  <li>TFT_MOSI 13</li>
  <li>TFT_DC 0</li>
  <li>TFT_RST 2</li>
  <li>TFT_BACKLIGHT 5</li>
  <li>TFT_CS 15</li>
</ul>

<p>Now we’re talking.</p>

<p>Our brand new <code class="language-plaintext highlighter-rouge">main.c</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;Arduino.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Adafruit_GFX.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Adafruit_ST7789.h&gt;</span><span class="cp">
</span>
<span class="cp">#define TFT_CLK 14
#define TFT_MOSI 13
#define TFT_DC 0
#define TFT_RST 2
#define TFT_CS 15
#define TFT_BACKLIGHT 5
</span>
<span class="n">Adafruit_ST7789</span> <span class="n">tft</span> <span class="o">=</span> <span class="n">Adafruit_ST7789</span><span class="p">(</span><span class="n">TFT_CS</span><span class="p">,</span> <span class="n">TFT_DC</span><span class="p">,</span> <span class="n">TFT_RST</span><span class="p">);</span>

<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">pinMode</span><span class="p">(</span><span class="n">TFT_BACKLIGHT</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
  <span class="n">digitalWrite</span><span class="p">(</span><span class="n">TFT_BACKLIGHT</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
  <span class="n">tft</span><span class="p">.</span><span class="n">init</span><span class="p">(</span><span class="mi">240</span><span class="p">,</span> <span class="mi">240</span><span class="p">,</span> <span class="n">SPI_MODE3</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">tft</span><span class="p">.</span><span class="n">fillScreen</span><span class="p">(</span><span class="n">ST77XX_BLACK</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="n">tft</span><span class="p">.</span><span class="n">fillScreen</span><span class="p">(</span><span class="n">ST77XX_WHITE</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="n">tft</span><span class="p">.</span><span class="n">fillScreen</span><span class="p">(</span><span class="n">ST77XX_YELLOW</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="n">tft</span><span class="p">.</span><span class="n">fillScreen</span><span class="p">(</span><span class="n">ST77XX_GREEN</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="n">tft</span><span class="p">.</span><span class="n">fillScreen</span><span class="p">(</span><span class="n">ST77XX_BLUE</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="n">tft</span><span class="p">.</span><span class="n">fillScreen</span><span class="p">(</span><span class="n">ST77XX_RED</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It work!!! Great. My own MiniTv firmware. NICE!
But what I can display on it?</p>

<p>Yes! PIX! My other project uses ESP with led matrix.</p>

<p>https://github.com/fazibear/pix/tree/master/pix_esp8266</p>

<p>I just need to write another front end to display things on tft display. Let’s do IT.</p>

<p>FEW HOURS LATER</p>

<p>Voila! Yet another interesting project without AI :P</p>

<p><img src="/images/minitv_weather.jpg" alt="minitv" />
<img src="/images/minitv_crab.jpg" alt="minitv" /></p>

<p>Whoops. It’s 4AM. I need to sleep.</p>

<p><a href="https://github.com/fazibear/pix/blob/master/pix_esp8266/src/tft/tft.cpp">Here</a> is the code.</p>

<p>Good Night!</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="esp8266" /><category term="st7789" /><category term="tft" /><summary type="html"><![CDATA[Got one! And you know what? Firmware is not open source. I need to write my own right?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/smalltv.png" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/smalltv.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Pitara - Kitara Resurrected</title><link href="https://blog.fazibear.me/pitara-kitara-resurrected/" rel="alternate" type="text/html" title="Pitara - Kitara Resurrected" /><published>2024-08-20T00:00:00+00:00</published><updated>2024-08-20T00:00:00+00:00</updated><id>https://blog.fazibear.me/pitara-kitara-resurrected</id><content type="html" xml:base="https://blog.fazibear.me/pitara-kitara-resurrected/"><![CDATA[<p>I’m not so happy owner of Misa Digital first Kitara. Why not hapy? Because it’s not working.
Almost all Kitaras and TriBasses stop working because of touch panel failure.
Unfortunately there is no replacement. Those screens are not in production for a long time.</p>

<p>I want my Kitara back. And this is how it’s started.</p>

<h2 id="software">Software</h2>

<p>Software for Kitara suppose to be open source. It’s not available now. I found one on my old disk.
Turns out it’s use less. Source is for parts of user interface. There is no drivers or things like that.
Looks like I can do much about this. It’s useless.</p>

<h2 id="hardware">Hardware</h2>
<p>What we got here?</p>

<ul>
  <li>Main board</li>
  <li>Screen with broken touchscreen</li>
  <li>The case</li>
  <li>Neck with mysterious board</li>
  <li>Few I/O boards</li>
</ul>

<p>Mainboard is useless, Raspberry PI would be better :)
Screen. Works, but still touchscreen missing. It’s easier to get new one with touchpanel.
Case. Goal is to fit in this case.
Neck. I need. This part make this thing a guitar.</p>

<p>Everything is clear. The goal is:</p>

<ul>
  <li>RPi Zero as a heart</li>
  <li>New touchscreen</li>
  <li>Use neck</li>
  <li>Fit in case</li>
</ul>

<p>Because new screen shape is a little different I need to make a screen hole bigger, print a bazel and glue it into the main case.
Looks like everything fits. Just need to make all those things work together.</p>

<p>See you in the next one.</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="kitara" /><summary type="html"><![CDATA[I’m not so happy owner of Misa Digital first Kitara. Why not hapy? Because it’s not working. Almost all Kitaras and TriBasses stop working because of touch panel failure. Unfortunately there is no replacement. Those screens are not in production for a long time.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/kitara.jpg" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/kitara.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to cross compile linux kernel on MacOS</title><link href="https://blog.fazibear.me/how-to-cross-compile-linux-kernel-on-macos/" rel="alternate" type="text/html" title="How to cross compile linux kernel on MacOS" /><published>2024-08-15T00:00:00+00:00</published><updated>2024-08-15T00:00:00+00:00</updated><id>https://blog.fazibear.me/how-to-cross-compile-linux-kernel-on-macos</id><content type="html" xml:base="https://blog.fazibear.me/how-to-cross-compile-linux-kernel-on-macos/"><![CDATA[<p>You have a mac? You need to compile linux kernel for your Raspberry PI?</p>

<p>Great. Follow me…</p>

<!--more-->

<h3 id="part-1-case-sensitive-file-system">Part 1. Case sensitive file system</h3>

<p>Note: This step is required if you want to have kernel files outside docker.
There is a case sensitive fs inside docker container.
You can just copy needed file at the end.</p>

<p>Bad news. MacOS uses case insensitive file system by default. What we can do about it?
No we will not make whole root partition case sensitive. We might have trouble.
We try USB drive or SD card, but I don’t want to get up from a chair.</p>

<p>We need to create an image file with case sensitive file system first:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>hdiutil create <span class="nt">-size</span> 5g <span class="nt">-fs</span> <span class="s2">"Case-sensitive APFS"</span> <span class="nt">-volname</span> LinuxBuilder LinuxBuilder.dmg
</code></pre></div></div>

<p>Then we need to mount it:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>hdiutil attach LinuxBuilder.dmg <span class="nt">-mountpoint</span> linux_builder <span class="nt">-nobrowse</span> <span class="nt">-readwrite</span>
</code></pre></div></div>

<p>Enter the new file system</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>linux_builder
</code></pre></div></div>

<p>OK problem solved.</p>

<h3 id="part-2-checkout-kernel">Part 2. Checkout kernel</h3>

<p>Within aur new shiny file system checkout a kernel source:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> git clone <span class="nt">--depth</span> 1 https://github.com/raspberrypi/linux
</code></pre></div></div>

<h3 id="part-3-cross-compilation-tools">Part 3. Cross compilation tools</h3>

<p>I bet there are, but don’t want to think about this where they are, how to install them. We have docker. Right?
I have you can install it too. It’s easy. Just do it.</p>

<p>Create a “Dockerfile” file:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> debian:buster</span>

<span class="k">ENV</span><span class="s"> DEBIAN_FRONTEND=noninteractive</span>

<span class="k">RUN </span>apt-get clean <span class="o">&amp;&amp;</span> apt-get update <span class="o">&amp;&amp;</span> <span class="se">\
</span>    apt-get <span class="nb">install</span> <span class="nt">-y</span> <span class="se">\
</span>    bc <span class="se">\
</span>    bison <span class="se">\
</span>    flex <span class="se">\
</span>    libssl-dev <span class="se">\
</span>    make <span class="se">\
</span>    kmod <span class="se">\
</span>    libc6-dev <span class="se">\
</span>    libncurses5-dev <span class="se">\
</span>    crossbuild-essential-armhf <span class="se">\
</span>    crossbuild-essential-arm64

<span class="k">WORKDIR</span><span class="s"> /linux</span>
<span class="k">VOLUME</span><span class="s"> ["/linux"]</span>
</code></pre></div></div>

<p>Build an image:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker build <span class="nt">-t</span> linux_builder <span class="nb">.</span>
</code></pre></div></div>

<h3 id="part-4-make-script">Part 4. Make script</h3>

<p>We will need one command: <code class="language-plaintext highlighter-rouge">make</code>. But let’s create a simple wrapper that runs <code class="language-plaintext highlighter-rouge">make</code> in the container.
I am so creative, will call this file <code class="language-plaintext highlighter-rouge">make</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Run builder environment</span>
docker run <span class="nt">--rm</span> <span class="se">\</span>
    <span class="nt">--device</span> /dev/fuse <span class="se">\</span>
    <span class="nt">--cap-add</span> SYS_ADMIN <span class="se">\</span>
    <span class="nt">--name</span> linux_builder <span class="se">\</span>
    <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">/linux"</span>:<span class="s2">"/linux"</span> <span class="se">\</span>
    <span class="nt">-e</span> <span class="nv">ARCH</span><span class="o">=</span>arm <span class="se">\</span>
    <span class="nt">-e</span> <span class="nv">KERNEL</span><span class="o">=</span>kernel7 <span class="se">\</span>
    <span class="nt">-e</span> <span class="nv">CROSS_COMPILE</span><span class="o">=</span>arm-linux-gnueabihf- <span class="se">\</span>
    <span class="nt">-it</span> linux_builder <span class="se">\</span>
    make <span class="nv">$@</span>
</code></pre></div></div>

<p>This example is for RPI3. For other platform just adjust <code class="language-plaintext highlighter-rouge">ARCH</code>, <code class="language-plaintext highlighter-rouge">KERNEL</code>, and <code class="language-plaintext highlighter-rouge">CROSS_COMPILE</code> envs.</p>

<p>Make it executable:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ chmod +x make
</code></pre></div></div>

<p>We’re done.</p>

<h3 id="part-5-build-the-kernel">Part 5. Build the kernel</h3>

<p>Now instead use <code class="language-plaintext highlighter-rouge">make menuconfig</code> we will use <code class="language-plaintext highlighter-rouge">./make menuconfig</code>.
You know what to do next. Right?</p>

<p>If not, here is a hint:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./make bcm2709_defconfig
<span class="nv">$ </span>./make <span class="nt">-j12</span> zImage modules dtbs
</code></pre></div></div>

<p>Wait. And look what is there:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">ls </span>linux/arch/arm/boot/
</code></pre></div></div>

<p>This is all we want.
Cheers!</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="rpi" /><category term="linux" /><category term="kernel" /><category term="docker" /><summary type="html"><![CDATA[You have a mac? You need to compile linux kernel for your Raspberry PI?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/crosscompile.png" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/crosscompile.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Zig! Can you C?</title><link href="https://blog.fazibear.me/zig-can-you-c/" rel="alternate" type="text/html" title="Zig! Can you C?" /><published>2024-04-04T00:00:00+00:00</published><updated>2024-04-04T00:00:00+00:00</updated><id>https://blog.fazibear.me/zig-can-you-c</id><content type="html" xml:base="https://blog.fazibear.me/zig-can-you-c/"><![CDATA[<p>I just want to develop something different. But what? Game, graphics, sound processing?
Surely you’ll say Rust! Yep done a lot of things in Rust. But maybe C?</p>

<p>Ohhh… Noooo…
Remember <code class="language-plaintext highlighter-rouge">Segmentation Fault</code>?
How are you going to manage depenencies?</p>

<p>OK, So try to use some C library in ZIG! How hard it will be? Let’s see.</p>

<p>Try to write somple app using <code class="language-plaintext highlighter-rouge">raylib</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir </span>ray_test_zig
<span class="nv">$ </span><span class="nb">cd </span>ray_test_zig
<span class="nv">$ </span>zig init-exe
</code></pre></div></div>

<p>Got a project. Try to run?</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zig build run
</code></pre></div></div>

<p>Yep it’s working.
We need to fetch and include <code class="language-plaintext highlighter-rouge">raylib</code> somehow.</p>

<p>Zig uses <code class="language-plaintext highlighter-rouge">zon</code> to fetch dependencies. Does it work with C libraries? Find out!</p>

<p>We need to provide where the lib is! Here it is:</p>

<p>Create <code class="language-plaintext highlighter-rouge">build.zig.zon</code> file.</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="p">{</span>
    <span class="p">.</span><span class="py">name</span> <span class="o">=</span> <span class="s">"ray_test_zig"</span><span class="p">,</span>
    <span class="p">.</span><span class="py">version</span> <span class="o">=</span> <span class="s">"0.0.1"</span><span class="p">,</span>
    <span class="p">.</span><span class="py">paths</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span><span class="s">""</span><span class="p">},</span>

    <span class="p">.</span><span class="py">dependencies</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span>
        <span class="p">.</span><span class="py">raylib</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span>
            <span class="p">.</span><span class="py">url</span> <span class="o">=</span> <span class="s">"https://github.com/raysan5/raylib/archive/efce4d69ce913bca42289184b0bffe4339c0193f.tar.gz"</span><span class="p">,</span>
        <span class="p">},</span>
    <span class="p">},</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Try to build project?</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zig build
</code></pre></div></div>

<p>What it is?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...build.zig.zon:7:20: error: dependency is missing hash field
            .url = "https://github.com/raysan5/raylib/archive/efce4d69ce913bca42289184b0bffe4339c0193f.tar.gz",
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: expected .hash = "1220055a322aac82f0c8104810bac892eaf62ab11582ca0f7bd1d04081048c67a081",

</code></pre></div></div>

<p>Ok, zon expects a hash, just in case someone will try to hack out computer. Once again:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="p">{</span>
    <span class="p">.</span><span class="py">name</span> <span class="o">=</span> <span class="s">"ray_test_zig"</span><span class="p">,</span>
    <span class="p">.</span><span class="py">version</span> <span class="o">=</span> <span class="s">"0.0.1"</span><span class="p">,</span>

    <span class="p">.</span><span class="py">dependencies</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span>
        <span class="p">.</span><span class="py">raylib</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span>
            <span class="p">.</span><span class="py">url</span> <span class="o">=</span> <span class="s">"https://github.com/raysan5/raylib/archive/efce4d69ce913bca42289184b0bffe4339c0193f.tar.gz"</span><span class="p">,</span>
            <span class="p">.</span><span class="py">hash</span> <span class="o">=</span> <span class="s">"1220055a322aac82f0c8104810bac892eaf62ab11582ca0f7bd1d04081048c67a081"</span><span class="p">,</span>
        <span class="p">},</span>
    <span class="p">},</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Try once again:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zig build
</code></pre></div></div>
<p>It works! Woooow! That’s it?</p>

<p>No! We need to tell zig to include <code class="language-plaintext highlighter-rouge">raylib</code> during build!</p>

<p>Now we will edit <code class="language-plaintext highlighter-rouge">build.zig</code>. Just above line ~30 we have <code class="language-plaintext highlighter-rouge">b.installArtifact(exe);</code>
Before that line we need to add:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">raylib</span> <span class="o">=</span> <span class="n">b</span><span class="p">.</span><span class="nf">dependency</span><span class="p">(</span><span class="s">"raylib"</span><span class="p">,</span> <span class="o">.</span><span class="p">{</span>
    <span class="p">.</span><span class="py">target</span> <span class="o">=</span> <span class="n">target</span><span class="p">,</span>
    <span class="p">.</span><span class="py">optimize</span> <span class="o">=</span> <span class="n">optimize</span><span class="p">,</span>
<span class="p">});</span>

<span class="n">exe</span><span class="p">.</span><span class="nf">installLibraryHeaders</span><span class="p">(</span><span class="n">raylib</span><span class="p">.</span><span class="nf">artifact</span><span class="p">(</span><span class="s">"raylib"</span><span class="p">));</span>
<span class="n">exe</span><span class="p">.</span><span class="nf">linkLibrary</span><span class="p">(</span><span class="n">raylib</span><span class="p">.</span><span class="nf">artifact</span><span class="p">(</span><span class="s">"raylib"</span><span class="p">));</span>

</code></pre></div></div>

<p>We’re teling zig where header files are and to link out executable with <code class="language-plaintext highlighter-rouge">raylib</code>.
Does it works? Let’s check!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zig build
</code></pre></div></div>

<p>OMG! Looks like somethings with raylib was happened. It’s compiled?
Let’s port an simple example from <code class="language-plaintext highlighter-rouge">raylib</code> to zig.</p>

<p>In the <code class="language-plaintext highlighter-rouge">src/main.zig</code>:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">std</span> <span class="o">=</span> <span class="nb">@import</span><span class="p">(</span><span class="s">"std"</span><span class="p">);</span>

<span class="k">const</span> <span class="n">ray</span> <span class="o">=</span> <span class="nb">@cImport</span><span class="p">({</span>
    <span class="nb">@cInclude</span><span class="p">(</span><span class="s">"raylib.h"</span><span class="p">);</span>
<span class="p">});</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="n">main</span><span class="p">()</span> <span class="o">!</span><span class="k">void</span> <span class="p">{</span>
    <span class="n">ray</span><span class="p">.</span><span class="nf">InitWindow</span><span class="p">(</span><span class="mi">800</span><span class="p">,</span> <span class="mi">450</span><span class="p">,</span> <span class="s">"Hey ZIG"</span><span class="p">);</span>
    <span class="k">defer</span> <span class="n">ray</span><span class="p">.</span><span class="nf">CloseWindow</span><span class="p">();</span>

    <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">ray</span><span class="p">.</span><span class="nf">WindowShouldClose</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">ray</span><span class="p">.</span><span class="nf">BeginDrawing</span><span class="p">();</span>
        <span class="n">ray</span><span class="p">.</span><span class="nf">ClearBackground</span><span class="p">(</span><span class="n">ray</span><span class="p">.</span><span class="py">RAYWHITE</span><span class="p">);</span>
        <span class="n">ray</span><span class="p">.</span><span class="nf">DrawText</span><span class="p">(</span><span class="s">"Congrats! You created your first window!"</span><span class="p">,</span> <span class="mi">190</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="n">ray</span><span class="p">.</span><span class="py">LIGHTGRAY</span><span class="p">);</span>
        <span class="n">ray</span><span class="p">.</span><span class="nf">EndDrawing</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zig build
</code></pre></div></div>

<p>No errors? Great!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zig build run
</code></pre></div></div>

<p>We got the raylib window!
As you can see! Just one line of code and <code class="language-plaintext highlighter-rouge">raylib</code> working like native lib!</p>

<p>So yep! Zig can C!</p>

<p>PS. raylib is not a good example. It have build.zig file included to make things easier ;]</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="c" /><category term="zig" /><category term="raylib" /><summary type="html"><![CDATA[I just want to develop something different. But what? Game, graphics, sound processing? Surely you’ll say Rust! Yep done a lot of things in Rust. But maybe C?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/raylib.png" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/raylib.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to generate git commit message using AI?</title><link href="https://blog.fazibear.me/how-to-generate-git-commit-message-using-ai/" rel="alternate" type="text/html" title="How to generate git commit message using AI?" /><published>2023-08-27T00:00:00+00:00</published><updated>2023-08-27T00:00:00+00:00</updated><id>https://blog.fazibear.me/how-to-generate-git-commit-message-using-ai</id><content type="html" xml:base="https://blog.fazibear.me/how-to-generate-git-commit-message-using-ai/"><![CDATA[<p>Why don’t use available solution? All of them using ChatGPT. But I’m out of credits ;)
Of course I want to learn something!</p>

<h2 id="how-to-generate-git-commit-message">How to generate git commit message?</h2>

<p>Git allows you to create hooks. Let’s use global one. Global hooks works without modifying every git repo.</p>

<p>Create a directory for hooks:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir</span> ~/.config/git/hooks/
</code></pre></div></div>
<p>Let git knows where hooks are:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git config core.hooksPath ~/.config/git/hooks/
</code></pre></div></div>

<p>Long story short the <code class="language-plaintext highlighter-rouge">prepare-commit-msg</code> is the one we need. The file we need to update is passed as first parameter.
Create a simple script:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>

<span class="nb">echo</span> <span class="s2">"Fancy commit message"</span> <span class="o">&gt;</span> <span class="nv">$1</span>
</code></pre></div></div>

<p>Make it executable:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">chmod</span> +z ~/.confog/git/hooks/prepare-commit-msg
</code></pre></div></div>
<p>Is it works? Let’s commit something … Yep, we have a message at the end of the commit message.</p>

<p>Let’s generate something:</p>

<h2 id="generating-commit-message">Generating commit message</h2>

<p>Let’s build something that’s works offline. AI? Yes, let’s use AI!</p>

<p>We need model right?</p>

<p>Let’s look at <a href="https://huggingface.co/models">huggingface</a>!</p>

<p>There it is: https://huggingface.co/mamiksik/T5-commit-message-generation but there are no documentation :(
But if you’ll look deeper you’ll find https://huggingface.co/spaces/mamiksik/commit-message-generator</p>

<p>We can use this https://huggingface.co/spaces/mamiksik/commit-message-generator/blob/main/app.py with a little modifications.</p>

<p>As we can use any shell script in a hook, let’s use python.</p>

<p>Let’s take a look what’s there:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">re</span>

<span class="kn">import</span> <span class="n">gradio</span> <span class="k">as</span> <span class="n">gr</span>
<span class="kn">import</span> <span class="n">torch</span>
<span class="kn">from</span> <span class="n">transformers</span> <span class="kn">import</span> <span class="n">T5ForConditionalGeneration</span><span class="p">,</span> <span class="n">RobertaTokenizer</span>


<span class="n">tokenizer</span> <span class="o">=</span> <span class="n">RobertaTokenizer</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">mamiksik/CommitPredictorT5PL</span><span class="sh">"</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="sh">"</span><span class="s">fb08d01</span><span class="sh">"</span><span class="p">)</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">T5ForConditionalGeneration</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">mamiksik/CommitPredictorT5PL</span><span class="sh">"</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="sh">"</span><span class="s">fb08d01</span><span class="sh">"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">parse_files</span><span class="p">(</span><span class="n">patch</span><span class="p">):</span>
    <span class="n">accumulator</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">lines</span> <span class="o">=</span> <span class="n">patch</span><span class="p">.</span><span class="nf">splitlines</span><span class="p">()</span>

    <span class="n">filename_before</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">index</span><span class="sh">"</span><span class="p">)</span> <span class="ow">or</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">diff</span><span class="sh">"</span><span class="p">):</span>
            <span class="k">continue</span>
        <span class="k">if</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">---</span><span class="sh">"</span><span class="p">):</span>
            <span class="n">filename_before</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">:]</span>
            <span class="k">continue</span>

        <span class="k">if</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">+++</span><span class="sh">"</span><span class="p">):</span>
            <span class="n">filename_after</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">:]</span>

            <span class="k">if</span> <span class="n">filename_before</span> <span class="o">==</span> <span class="n">filename_after</span><span class="p">:</span>
                <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">&lt;ide&gt;&lt;path&gt;</span><span class="si">{</span><span class="n">filename_before</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">&lt;add&gt;&lt;path&gt;</span><span class="si">{</span><span class="n">filename_after</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
                <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">&lt;del&gt;&lt;path&gt;</span><span class="si">{</span><span class="n">filename_before</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">continue</span>

        <span class="n">line</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nf">sub</span><span class="p">(</span><span class="sh">"</span><span class="s">@@[^@@]*@@</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
        <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">continue</span>

        <span class="k">if</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">+</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="sh">"</span><span class="s">+</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">&lt;add&gt;</span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">-</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="sh">"</span><span class="s">-</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">&lt;del&gt;</span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">&lt;ide&gt;</span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="sh">"</span>

        <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>

    <span class="k">return</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">accumulator</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">patch</span><span class="p">,</span> <span class="n">max_length</span><span class="p">,</span> <span class="n">min_length</span><span class="p">,</span> <span class="n">num_beams</span><span class="p">,</span> <span class="n">prediction_count</span><span class="p">):</span>
    <span class="n">input_text</span> <span class="o">=</span> <span class="nf">parse_files</span><span class="p">(</span><span class="n">patch</span><span class="p">)</span>
    <span class="k">with</span> <span class="n">torch</span><span class="p">.</span><span class="nf">no_grad</span><span class="p">():</span>
        <span class="n">token_count</span> <span class="o">=</span> <span class="nf">tokenizer</span><span class="p">(</span><span class="n">input_text</span><span class="p">,</span> <span class="n">return_tensors</span><span class="o">=</span><span class="sh">"</span><span class="s">pt</span><span class="sh">"</span><span class="p">).</span><span class="n">input_ids</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>

        <span class="n">input_ids</span> <span class="o">=</span> <span class="nf">tokenizer</span><span class="p">(</span>
            <span class="n">input_text</span><span class="p">,</span>
            <span class="n">truncation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
            <span class="n">padding</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
            <span class="n">return_tensors</span><span class="o">=</span><span class="sh">"</span><span class="s">pt</span><span class="sh">"</span><span class="p">,</span>
        <span class="p">).</span><span class="n">input_ids</span>

        <span class="n">outputs</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span>
            <span class="n">input_ids</span><span class="p">,</span>
            <span class="n">max_length</span><span class="o">=</span><span class="n">max_length</span><span class="p">,</span>
            <span class="n">min_length</span><span class="o">=</span><span class="n">min_length</span><span class="p">,</span>
            <span class="n">num_beams</span><span class="o">=</span><span class="n">num_beams</span><span class="p">,</span>
            <span class="n">num_return_sequences</span><span class="o">=</span><span class="n">prediction_count</span><span class="p">,</span>
        <span class="p">)</span>

    <span class="n">result</span> <span class="o">=</span> <span class="n">tokenizer</span><span class="p">.</span><span class="nf">batch_decode</span><span class="p">(</span><span class="n">outputs</span><span class="p">,</span> <span class="n">skip_special_tokens</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">token_count</span><span class="p">,</span> <span class="n">input_text</span><span class="p">,</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">result</span><span class="p">}</span>


<span class="n">iface</span> <span class="o">=</span> <span class="n">gr</span><span class="p">.</span><span class="nc">Interface</span><span class="p">(</span><span class="n">fn</span><span class="o">=</span><span class="n">predict</span><span class="p">,</span> <span class="n">inputs</span><span class="o">=</span><span class="p">[</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Textbox</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Patch (as generated by git diff)</span><span class="sh">"</span><span class="p">),</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Slider</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">40</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Max message length</span><span class="sh">"</span><span class="p">),</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Slider</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Min message length</span><span class="sh">"</span><span class="p">),</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Slider</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">7</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Number of beams</span><span class="sh">"</span><span class="p">),</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Slider</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Number of predictions</span><span class="sh">"</span><span class="p">),</span>
<span class="p">],</span> <span class="n">outputs</span><span class="o">=</span><span class="p">[</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Textbox</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Token count</span><span class="sh">"</span><span class="p">),</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Textbox</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Parsed patch</span><span class="sh">"</span><span class="p">),</span>
    <span class="n">gr</span><span class="p">.</span><span class="nc">Label</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="sh">"</span><span class="s">Predictions</span><span class="sh">"</span><span class="p">)</span>
<span class="p">],</span> <span class="n">examples</span><span class="o">=</span><span class="p">[</span>
<span class="p">[</span><span class="sh">"""</span><span class="s">
diff --git a/.github/workflows/pylint.yml b/.github/workflows/codestyle_checks.yml
similarity index 86%
rename from .github/workflows/pylint.yml
rename to .github/workflows/codestyle_checks.yml
index a5d5c4d9..8cbf9713 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/codestyle_checks.yml
@@ -20,3 +20,6 @@ jobs:
     - name: Analysing the code with pylint
       run: |
         pylint --rcfile=.pylintrc webapp core
+    - name: Analysing the code with flake8
+      run: |
+        flake8
</span><span class="sh">"""</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
<span class="p">]</span>
<span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="n">iface</span><span class="p">.</span><span class="nf">launch</span><span class="p">()</span>
</code></pre></div></div>
<p>Everything we need is here! We need to:</p>

<ul>
  <li>fetch gitmessage file to update</li>
  <li>fetch git diff</li>
  <li>use current script make predictions</li>
  <li>prepend commit message to gitmessage file</li>
</ul>

<h4 id="file-that-we-need-to-update-is-passed-as-first-parameter-so">File that we need to update is passed as first parameter so</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">sys</span>

<span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</code></pre></div></div>
<p>Heh that was easy.</p>

<h4 id="fetch-git-diff">Fetch git diff</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">subprocess</span>

<span class="n">subprocess</span><span class="p">.</span><span class="nf">run</span><span class="p">([</span><span class="sh">'</span><span class="s">git</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">diff</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">--cached</span><span class="sh">'</span><span class="p">],</span> <span class="n">capture_output</span><span class="o">=</span><span class="bp">True</span><span class="p">).</span><span class="n">stdout</span><span class="p">.</span><span class="nf">decode</span><span class="p">(</span><span class="sh">'</span><span class="s">utf-8</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>

<p>Easy peasy!</p>

<h4 id="use-current-script-to-make-predictions">Use current script to make predictions</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">max_message</span> <span class="o">=</span> <span class="mi">40</span>
<span class="n">min_message</span> <span class="o">=</span> <span class="mi">5</span>
<span class="n">num_beams</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">num_predictions</span> <span class="o">=</span> <span class="mi">1</span>

<span class="n">msg</span> <span class="o">=</span> <span class="nf">predict</span><span class="p">(</span><span class="n">diff</span><span class="p">,</span> <span class="n">max_message</span><span class="p">,</span> <span class="n">min_message</span><span class="p">,</span> <span class="n">num_beams</span><span class="p">,</span> <span class="n">num_predictions</span><span class="p">)</span>
</code></pre></div></div>

<h4 id="prepend-our-message-to-gitmessage-file">Prepend our message to gitmessage file</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="sh">'</span><span class="s">r+</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">content</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">seek</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">msg</span> <span class="o">+</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span> <span class="o">+</span> <span class="n">content</span><span class="p">)</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>
</code></pre></div></div>

<p>It’s just like that. With little cleanups this is our final script.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python
</span><span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Generating commit message</span><span class="sh">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">""</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="kn">import</span> <span class="n">sys</span>
<span class="kn">import</span> <span class="n">re</span>
<span class="kn">import</span> <span class="n">subprocess</span>
<span class="kn">import</span> <span class="n">torch</span>
<span class="kn">from</span> <span class="n">transformers</span> <span class="kn">import</span> <span class="n">T5ForConditionalGeneration</span><span class="p">,</span> <span class="n">RobertaTokenizer</span>

<span class="k">def</span> <span class="nf">parse_files</span><span class="p">(</span><span class="n">patch</span><span class="p">):</span>
    <span class="n">accumulator</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">lines</span> <span class="o">=</span> <span class="n">patch</span><span class="p">.</span><span class="nf">splitlines</span><span class="p">()</span>

    <span class="n">filename_before</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">""</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">index</span><span class="sh">"</span><span class="p">)</span> <span class="ow">or</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">diff</span><span class="sh">"</span><span class="p">):</span>
            <span class="k">continue</span>
        <span class="k">if</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">---</span><span class="sh">"</span><span class="p">):</span>
            <span class="n">filename_before</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">:]</span>
            <span class="k">continue</span>

        <span class="k">if</span> <span class="n">line</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">+++</span><span class="sh">"</span><span class="p">):</span>
            <span class="n">filename_after</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">:]</span>

            <span class="k">if</span> <span class="n">filename_before</span> <span class="o">==</span> <span class="n">filename_after</span><span class="p">:</span>
                <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">&lt;ide&gt;&lt;path&gt;</span><span class="si">{</span><span class="n">filename_before</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">&lt;add&gt;&lt;path&gt;</span><span class="si">{</span><span class="n">filename_after</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
                <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">&lt;del&gt;&lt;path&gt;</span><span class="si">{</span><span class="n">filename_before</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">continue</span>

        <span class="n">line</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nf">sub</span><span class="p">(</span><span class="sh">"</span><span class="s">@@[^@@]*@@</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
        <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">continue</span>

        <span class="k">if</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">+</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="sh">"</span><span class="s">+</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">&lt;add&gt;</span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">-</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="sh">"</span><span class="s">-</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">&lt;del&gt;</span><span class="sh">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">&lt;ide&gt;</span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="sh">"</span>

        <span class="n">accumulator</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>

    <span class="k">return</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">accumulator</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">patch</span><span class="p">,</span> <span class="n">max_length</span><span class="p">,</span> <span class="n">min_length</span><span class="p">,</span> <span class="n">num_beams</span><span class="p">,</span> <span class="n">prediction_count</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">""</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">input_text</span> <span class="o">=</span> <span class="nf">parse_files</span><span class="p">(</span><span class="n">patch</span><span class="p">)</span>
    
    <span class="n">tokenizer</span> <span class="o">=</span> <span class="n">RobertaTokenizer</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">mamiksik/CommitPredictorT5PL</span><span class="sh">"</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="sh">"</span><span class="s">fb08d01</span><span class="sh">"</span><span class="p">,</span> <span class="n">low_cpu_mem_usage</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">""</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">T5ForConditionalGeneration</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">mamiksik/CommitPredictorT5PL</span><span class="sh">"</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="sh">"</span><span class="s">fb08d01</span><span class="sh">"</span><span class="p">,</span> <span class="n">low_cpu_mem_usage</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">""</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

    <span class="k">with</span> <span class="n">torch</span><span class="p">.</span><span class="nf">no_grad</span><span class="p">():</span>
        <span class="n">input_ids</span> <span class="o">=</span> <span class="nf">tokenizer</span><span class="p">(</span>
            <span class="n">input_text</span><span class="p">,</span>
            <span class="n">truncation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
            <span class="n">padding</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
            <span class="n">return_tensors</span><span class="o">=</span><span class="sh">"</span><span class="s">pt</span><span class="sh">"</span><span class="p">,</span>
        <span class="p">).</span><span class="n">input_ids</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">""</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">outputs</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span>
            <span class="n">input_ids</span><span class="p">,</span>
            <span class="n">max_length</span><span class="o">=</span><span class="n">max_length</span><span class="p">,</span>
            <span class="n">min_length</span><span class="o">=</span><span class="n">min_length</span><span class="p">,</span>
            <span class="n">num_beams</span><span class="o">=</span><span class="n">num_beams</span><span class="p">,</span>
            <span class="n">num_return_sequences</span><span class="o">=</span><span class="n">prediction_count</span><span class="p">,</span>
        <span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">""</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

    <span class="n">result</span> <span class="o">=</span> <span class="n">tokenizer</span><span class="p">.</span><span class="nf">batch_decode</span><span class="p">(</span><span class="n">outputs</span><span class="p">,</span> <span class="n">skip_special_tokens</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="n">diff</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="nf">run</span><span class="p">([</span><span class="sh">'</span><span class="s">git</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">diff</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">--cached</span><span class="sh">'</span><span class="p">],</span> <span class="n">capture_output</span><span class="o">=</span><span class="bp">True</span><span class="p">).</span><span class="n">stdout</span><span class="p">.</span><span class="nf">decode</span><span class="p">(</span><span class="sh">'</span><span class="s">utf-8</span><span class="sh">'</span><span class="p">)</span>

    <span class="n">max_message</span> <span class="o">=</span> <span class="mi">40</span>
    <span class="n">min_message</span> <span class="o">=</span> <span class="mi">5</span>
    <span class="n">num_beams</span> <span class="o">=</span> <span class="mi">10</span>
    <span class="n">num_predictions</span> <span class="o">=</span> <span class="mi">1</span>

    <span class="n">msg</span> <span class="o">=</span> <span class="nf">predict</span><span class="p">(</span><span class="n">diff</span><span class="p">,</span> <span class="n">max_message</span><span class="p">,</span> <span class="n">min_message</span><span class="p">,</span> <span class="n">num_beams</span><span class="p">,</span> <span class="n">num_predictions</span><span class="p">)</span>

    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="sh">'</span><span class="s">r+</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">content</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>
        <span class="n">f</span><span class="p">.</span><span class="nf">seek</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">msg</span> <span class="o">+</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span> <span class="o">+</span> <span class="n">content</span><span class="p">)</span>
        <span class="n">f</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>

    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Done!</span><span class="se">\n</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>
<p>It’s fast on cpu, but loading model take a lot of times. Anyway 3s is OK.
That’s all. It works. At least for me.</p>

<p>One more thing!. Just figure it out you can use api directly from command line shell (fish).</p>

<pre><code class="language-fish">curl -s -X POST https://mamiksik-commit-message-generator.hf.space/run/predict
  -H 'Content-Type: application/json'
  -d '{ "data": ["(git diff)", 40, 5, 7, 1]}'
  | jq ".data[2].label"
</code></pre>]]></content><author><name>Michał Kalbarczyk</name></author><category term="git" /><category term="ai" /><category term="python" /><summary type="html"><![CDATA[Why don’t use available solution? All of them using ChatGPT. But I’m out of credits ;) Of course I want to learn something!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/aigit.jpg" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/aigit.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">When WASM gets Rusty?</title><link href="https://blog.fazibear.me/when-wasm-gets-rusty/" rel="alternate" type="text/html" title="When WASM gets Rusty?" /><published>2023-05-15T00:00:00+00:00</published><updated>2023-05-15T00:00:00+00:00</updated><id>https://blog.fazibear.me/when-wasm-gets-rusty</id><content type="html" xml:base="https://blog.fazibear.me/when-wasm-gets-rusty/"><![CDATA[<p>We will take a look at WASM again! But this time with Rust. Why Rust? Because I can compile Rust directly into WASM.</p>

<p>Our goal is the same app we’ve created in Go, but … yes! with Rust!</p>

<p>What is the plan?</p>

<p>Let’s try to create same app that we did last time in Go. The asciifyier. It ill convert data from video camera into asciiart.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cargo new --lib asciifier</span>
</code></pre></div></div>

<p>One more thing:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># rustup target install wasm32-unknown-unknown  </span>
</code></pre></div></div>
<p>Target is needed, so we can compile directly to WASM.</p>

<p>Before we go into the code we need to add dependencies into <code class="language-plaintext highlighter-rouge">Cargo.toml</code> file.</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[dependencies]</span>
<span class="py">wasm-bindgen</span> <span class="p">=</span> <span class="s">"0.2.63"</span>
<span class="py">wasm-bindgen-futures</span> <span class="p">=</span> <span class="s">"0.4.15"</span>
<span class="py">js-sys</span> <span class="p">=</span> <span class="s">"0.3.35"</span>
<span class="py">console_error_panic_hook</span> <span class="p">=</span> <span class="s">"0.1.7"</span>
<span class="py">log</span> <span class="p">=</span> <span class="s">"0.4"</span>

<span class="py">console_log</span> <span class="o">=</span> <span class="p">{</span> <span class="py">version</span> <span class="p">=</span> <span class="s">"1"</span><span class="p">,</span> <span class="py">features</span> <span class="p">=</span> <span class="p">[</span>
  <span class="s">'color'</span>
<span class="p">]}</span>

<span class="py">web-sys</span> <span class="o">=</span> <span class="p">{</span> <span class="py">version</span> <span class="p">=</span> <span class="s">"0.3.4"</span><span class="p">,</span> <span class="py">features</span> <span class="p">=</span> <span class="p">[</span>
  <span class="s">'Document'</span><span class="p">,</span>
  <span class="s">'Element'</span><span class="p">,</span>
  <span class="s">'GetUserMediaRequest'</span><span class="p">,</span>
  <span class="s">'HtmlCanvasElement'</span><span class="p">,</span>
  <span class="s">'HtmlMediaElement'</span><span class="p">,</span>
  <span class="s">'HtmlVideoElement'</span><span class="p">,</span>
  <span class="s">'HtmlElement'</span><span class="p">,</span>
  <span class="s">'MediaStreamConstraints'</span><span class="p">,</span>
  <span class="s">'MediaDevices'</span><span class="p">,</span>
  <span class="s">'Navigator'</span><span class="p">,</span>
  <span class="s">'Node'</span><span class="p">,</span>
  <span class="s">'Window'</span><span class="p">,</span>
  <span class="s">'MediaStream'</span><span class="p">,</span>
  <span class="s">'CanvasRenderingContext2d'</span><span class="p">,</span>
  <span class="s">'ImageData'</span><span class="p">,</span>
  <span class="s">'console'</span><span class="p">,</span>
  <span class="s">'ContextAttributes2d'</span><span class="p">,</span>
<span class="p">]}</span>
</code></pre></div></div>

<p>It’s clear there are libs that our project will use but what about <code class="language-plaintext highlighter-rouge">features</code>? When we specify <code class="language-plaintext highlighter-rouge">features</code>, compiler will compile in these so we can use them.
Anyway our project uses only libs that allows us to use what browser offer.</p>

<p>Divide our project to 3 parts:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">webcam</code> - fetch image from web cam</li>
  <li><code class="language-plaintext highlighter-rouge">canvas</code> - fetch pixel data from image</li>
  <li><code class="language-plaintext highlighter-rouge">asciifyier</code> - turn image data into string</li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">canvas.rs</code>:</p>

<p>We need:</p>
<ul>
  <li>create <code class="language-plaintext highlighter-rouge">canvas</code> element</li>
  <li>get <code class="language-plaintext highlighter-rouge">2d</code> context</li>
  <li>function to draw <code class="language-plaintext highlighter-rouge">video</code> frame on canvas</li>
  <li>function to fetch pixel data from canvas</li>
</ul>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">wasm_bindgen</span><span class="p">::{</span><span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">,</span> <span class="n">Clamped</span><span class="p">};</span>
<span class="k">use</span> <span class="nn">web_sys</span><span class="p">::{</span><span class="n">window</span><span class="p">,</span> <span class="n">CanvasRenderingContext2d</span><span class="p">,</span> <span class="n">HtmlCanvasElement</span><span class="p">,</span> <span class="n">HtmlVideoElement</span><span class="p">};</span>

<span class="k">use</span> <span class="k">crate</span><span class="p">::{</span><span class="n">CANVAS_HEIGHT</span><span class="p">,</span> <span class="n">CANVAS_WIDTH</span><span class="p">};</span>

<span class="nd">#[derive(Debug)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">Canvas</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="n">context</span><span class="p">:</span> <span class="n">CanvasRenderingContext2d</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Canvas</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">document</span> <span class="o">=</span> <span class="nf">window</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.document</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>

        <span class="k">let</span> <span class="k">mut</span> <span class="n">context_attributes</span> <span class="o">=</span> <span class="nn">web_sys</span><span class="p">::</span><span class="nn">ContextAttributes2d</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
        <span class="n">context_attributes</span><span class="nf">.will_read_frequently</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>

        <span class="c1">// create document element</span>
        <span class="k">let</span> <span class="n">canvas</span> <span class="o">=</span> <span class="n">document</span>
            <span class="nf">.create_element</span><span class="p">(</span><span class="s">"canvas"</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="py">.dyn_into</span><span class="p">::</span><span class="o">&lt;</span><span class="n">HtmlCanvasElement</span><span class="o">&gt;</span><span class="p">()</span>
            <span class="nf">.unwrap</span><span class="p">();</span>

        <span class="c1">// set dimensions</span>
        <span class="n">canvas</span><span class="nf">.set_width</span><span class="p">(</span><span class="n">CANVAS_WIDTH</span><span class="nf">.into</span><span class="p">());</span>
        <span class="n">canvas</span><span class="nf">.set_height</span><span class="p">(</span><span class="n">CANVAS_HEIGHT</span><span class="nf">.into</span><span class="p">());</span>

        <span class="c1">// get 2d context</span>
        <span class="k">let</span> <span class="n">context</span> <span class="o">=</span> <span class="n">canvas</span>
            <span class="nf">.get_context_with_context_options</span><span class="p">(</span><span class="s">"2d"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">context_attributes</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="py">.dyn_into</span><span class="p">::</span><span class="o">&lt;</span><span class="n">CanvasRenderingContext2d</span><span class="o">&gt;</span><span class="p">()</span>
            <span class="nf">.unwrap</span><span class="p">();</span>

        <span class="c1">// store the context</span>
        <span class="k">Self</span> <span class="p">{</span> <span class="n">context</span> <span class="p">}</span>
    <span class="p">}</span>
  
    <span class="c1">// draw a video frame on canvas</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">draw_image</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">Self</span><span class="p">,</span> <span class="n">video</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">HtmlVideoElement</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.context</span>
            <span class="nf">.draw_image_with_html_video_element_and_dw_and_dh</span><span class="p">(</span>
                <span class="n">video</span><span class="p">,</span>
                <span class="mf">0.0</span><span class="p">,</span>
                <span class="mf">0.0</span><span class="p">,</span>
                <span class="n">CANVAS_WIDTH</span> <span class="k">as</span> <span class="nb">f64</span><span class="p">,</span>
                <span class="n">CANVAS_HEIGHT</span> <span class="k">as</span> <span class="nb">f64</span><span class="p">,</span>
            <span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="c1">// fetch pixel data</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">get_image_data</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">Self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Clamped</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.context</span>
            <span class="nf">.get_image_data</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="n">CANVAS_WIDTH</span> <span class="k">as</span> <span class="nb">f64</span><span class="p">,</span> <span class="n">CANVAS_HEIGHT</span> <span class="k">as</span> <span class="nb">f64</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="nf">.data</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Important thing is that you need to use <code class="language-plaintext highlighter-rouge">dyn_into</code> to make created element have a specific type. Other than that whole implementation is easy to read.</p>

<p>The <code class="language-plaintext highlighter-rouge">asciifier</code> takes a pixel data and converts it into <code class="language-plaintext highlighter-rouge">String</code>.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">wasm_bindgen</span><span class="p">::</span><span class="n">Clamped</span><span class="p">;</span>

<span class="k">const</span> <span class="n">CHARS_LENGTH</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
<span class="k">const</span> <span class="k">fn</span> <span class="nf">get_char</span><span class="p">(</span><span class="n">index</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">char</span> <span class="p">{</span>
    <span class="s">"   .,:;i1tfLCG08@"</span><span class="nf">.as_bytes</span><span class="p">()[</span><span class="n">index</span><span class="p">]</span> <span class="k">as</span> <span class="nb">char</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="nf">process</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Clamped</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">output</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>

    <span class="k">for</span> <span class="n">y</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">40</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">80</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">y</span> <span class="o">*</span> <span class="mi">80</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>

            <span class="k">let</span> <span class="n">red</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">offset</span><span class="p">];</span>
            <span class="k">let</span> <span class="n">green</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">offset</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
            <span class="k">let</span> <span class="n">blue</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">offset</span> <span class="o">+</span> <span class="mi">2</span><span class="p">];</span>
            <span class="c1">//let alpha = data[offset+3]</span>

            <span class="k">let</span> <span class="n">brightness</span> <span class="o">=</span> <span class="p">(</span><span class="mf">0.3</span> <span class="o">*</span> <span class="n">red</span> <span class="k">as</span> <span class="nb">f32</span> <span class="o">+</span> <span class="mf">0.59</span> <span class="o">*</span> <span class="n">green</span> <span class="k">as</span> <span class="nb">f32</span> <span class="o">+</span> <span class="mf">0.11</span> <span class="o">*</span> <span class="n">blue</span> <span class="k">as</span> <span class="nb">f32</span><span class="p">)</span> <span class="o">/</span> <span class="mf">255.0</span><span class="p">;</span>

            <span class="k">let</span> <span class="n">char_index</span> <span class="o">=</span> <span class="n">CHARS_LENGTH</span> <span class="o">-</span> <span class="p">(</span><span class="n">brightness</span> <span class="o">*</span> <span class="n">CHARS_LENGTH</span> <span class="k">as</span> <span class="nb">f32</span><span class="p">)</span> <span class="k">as</span> <span class="nb">usize</span><span class="p">;</span>

            <span class="n">output</span><span class="nf">.push</span><span class="p">(</span><span class="nf">get_char</span><span class="p">(</span><span class="n">char_index</span><span class="p">));</span>
        <span class="p">}</span>
        <span class="n">output</span><span class="nf">.push</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">output</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Last thing? The <code class="language-plaintext highlighter-rouge">webcam</code>:</p>

<p>This module will create <code class="language-plaintext highlighter-rouge">video</code> element and set it up. Get the video stream from <code class="language-plaintext highlighter-rouge">getUserMedia</code> and pass it back into <code class="language-plaintext highlighter-rouge">video</code>.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">wasm_bindgen</span><span class="p">::</span><span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">web_sys</span><span class="p">::{</span><span class="n">window</span><span class="p">,</span> <span class="n">HtmlVideoElement</span><span class="p">,</span> <span class="n">MediaStreamConstraints</span><span class="p">};</span>

<span class="nd">#[derive(Debug)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">WebCam</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="n">video</span><span class="p">:</span> <span class="n">HtmlVideoElement</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">WebCam</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">document</span> <span class="o">=</span> <span class="nf">window</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.document</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>

        <span class="k">let</span> <span class="n">video</span> <span class="o">=</span> <span class="n">document</span>
            <span class="nf">.create_element</span><span class="p">(</span><span class="s">"video"</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="py">.dyn_into</span><span class="p">::</span><span class="o">&lt;</span><span class="n">HtmlVideoElement</span><span class="o">&gt;</span><span class="p">()</span>
            <span class="nf">.unwrap</span><span class="p">();</span>

        <span class="n">video</span><span class="nf">.set_autoplay</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>

        <span class="k">Self</span> <span class="p">{</span> <span class="n">video</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">async</span> <span class="k">fn</span> <span class="nf">setup</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span> <span class="n">JsValue</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">constraints</span> <span class="o">=</span> <span class="nn">MediaStreamConstraints</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
        <span class="n">constraints</span><span class="nf">.video</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">JsValue</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="k">true</span><span class="p">));</span>

        <span class="k">let</span> <span class="n">promise</span> <span class="o">=</span> <span class="nf">window</span><span class="p">()</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="nf">.navigator</span><span class="p">()</span>
            <span class="nf">.media_devices</span><span class="p">()</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="nf">.get_user_media_with_constraints</span><span class="p">(</span><span class="o">&amp;</span><span class="n">constraints</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">();</span>

        <span class="k">let</span> <span class="n">stream</span> <span class="o">=</span> <span class="nn">wasm_bindgen_futures</span><span class="p">::</span><span class="nn">JsFuture</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">promise</span><span class="p">)</span><span class="k">.await</span><span class="o">?</span><span class="p">;</span>

        <span class="k">self</span><span class="py">.video</span><span class="nf">.set_src_object</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stream</span><span class="nf">.into</span><span class="p">()));</span>

        <span class="nf">Ok</span><span class="p">(())</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And this is where things get complicated. <code class="language-plaintext highlighter-rouge">getUserMedia</code> returns a promise. We need to wait for it! But <code class="language-plaintext highlighter-rouge">await</code> works only within async functions. Let’s mars setup function as <code class="language-plaintext highlighter-rouge">async</code> then.</p>

<p>Making it work together.</p>

<p>We need to create a loop using <code class="language-plaintext highlighter-rouge">request_animation_frame</code> that:</p>
<ul>
  <li>draw video frame on canvas</li>
  <li>takes pixel data from <code class="language-plaintext highlighter-rouge">canvas</code></li>
  <li>pass it thru <code class="language-plaintext highlighter-rouge">asciifier</code></li>
  <li>print string within <code class="language-plaintext highlighter-rouge">pre</code> element</li>
</ul>

<p>Here what it’s looks like:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">extern</span> <span class="k">crate</span> <span class="n">console_error_panic_hook</span><span class="p">;</span>

<span class="k">mod</span> <span class="n">asciifyier</span><span class="p">;</span>
<span class="k">mod</span> <span class="n">canvas</span><span class="p">;</span>
<span class="k">mod</span> <span class="n">utils</span><span class="p">;</span>
<span class="k">mod</span> <span class="n">web_cam</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">cell</span><span class="p">::</span><span class="n">RefCell</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">rc</span><span class="p">::</span><span class="nb">Rc</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">wasm_bindgen</span><span class="p">::</span><span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">web_sys</span><span class="p">::{</span><span class="n">window</span><span class="p">,</span> <span class="n">HtmlElement</span><span class="p">};</span>

<span class="k">const</span> <span class="n">CANVAS_WIDTH</span><span class="p">:</span> <span class="nb">u16</span> <span class="o">=</span> <span class="mi">80</span><span class="p">;</span>
<span class="k">const</span> <span class="n">CANVAS_HEIGHT</span><span class="p">:</span> <span class="nb">u16</span> <span class="o">=</span> <span class="mi">40</span><span class="p">;</span>

<span class="k">fn</span> <span class="nf">request_animation_frame</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Closure</span><span class="o">&lt;</span><span class="k">dyn</span> <span class="nf">FnMut</span><span class="p">()</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">window</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="nf">.request_animation_frame</span><span class="p">(</span><span class="n">f</span><span class="nf">.as_ref</span><span class="p">()</span><span class="nf">.unchecked_ref</span><span class="p">())</span>
        <span class="nf">.expect</span><span class="p">(</span><span class="s">"should register `requestAnimationFrame` OK"</span><span class="p">);</span>
<span class="p">}</span>

<span class="nd">#[wasm_bindgen(start)]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">run</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span> <span class="n">JsValue</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="nn">utils</span><span class="p">::</span><span class="nf">set_panic_hook</span><span class="p">();</span>
    <span class="nn">console_log</span><span class="p">::</span><span class="nf">init</span><span class="p">()</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"error initializing log"</span><span class="p">);</span>

    <span class="k">let</span> <span class="n">context</span> <span class="o">=</span> <span class="nn">canvas</span><span class="p">::</span><span class="nn">Canvas</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">web_cam</span> <span class="o">=</span> <span class="nn">web_cam</span><span class="p">::</span><span class="nn">WebCam</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">pre</span> <span class="o">=</span> <span class="nf">window</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="nf">.document</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="nf">.get_element_by_id</span><span class="p">(</span><span class="s">"pre"</span><span class="p">)</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="py">.dyn_into</span><span class="p">::</span><span class="o">&lt;</span><span class="n">HtmlElement</span><span class="o">&gt;</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="k">if</span> <span class="k">let</span> <span class="nf">Ok</span><span class="p">(())</span> <span class="o">=</span> <span class="n">web_cam</span><span class="nf">.setup</span><span class="p">()</span><span class="k">.await</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">f</span> <span class="o">=</span> <span class="nn">Rc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">RefCell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nb">None</span><span class="p">));</span>
        <span class="k">let</span> <span class="n">g</span> <span class="o">=</span> <span class="n">f</span><span class="nf">.clone</span><span class="p">();</span>

        <span class="o">*</span><span class="n">g</span><span class="nf">.borrow_mut</span><span class="p">()</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="nn">Closure</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
            <span class="nf">request_animation_frame</span><span class="p">(</span><span class="n">f</span><span class="nf">.borrow</span><span class="p">()</span><span class="nf">.as_ref</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">());</span>

            <span class="n">context</span><span class="nf">.draw_image</span><span class="p">(</span><span class="o">&amp;</span><span class="n">web_cam</span><span class="py">.video</span><span class="p">);</span>
            <span class="k">let</span> <span class="n">data</span> <span class="o">=</span> <span class="n">context</span><span class="nf">.get_image_data</span><span class="p">();</span>
            <span class="k">let</span> <span class="n">output</span> <span class="o">=</span> <span class="nn">asciifyier</span><span class="p">::</span><span class="nf">process</span><span class="p">(</span><span class="o">&amp;</span><span class="n">data</span><span class="p">);</span>

            <span class="n">pre</span><span class="nf">.set_inner_text</span><span class="p">(</span><span class="o">&amp;</span><span class="n">output</span><span class="p">);</span>
        <span class="p">}));</span>

        <span class="nf">request_animation_frame</span><span class="p">(</span><span class="n">g</span><span class="nf">.borrow</span><span class="p">()</span><span class="nf">.as_ref</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">());</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">pre</span><span class="nf">.set_inner_text</span><span class="p">(</span><span class="s">"Camera not found!"</span><span class="p">);</span>
    <span class="p">};</span>

    <span class="nf">Ok</span><span class="p">(())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We did it! But wait! <code class="language-plaintext highlighter-rouge">async fn run()</code>?. Yes! Because the target is WASM. We can have async main function. Browser will take care of it!</p>

<p>Compile time!</p>

<p><code class="language-plaintext highlighter-rouge">index.html</code> of course…</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;head&gt;</span>
    <span class="nt">&lt;title&gt;</span>asscify-me<span class="nt">&lt;/title&gt;</span>
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">'asciifyme.js'</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;script&gt;</span>
        <span class="nf">wasm_bindgen</span><span class="p">(</span><span class="dl">'</span><span class="s1">asciifyme_bg.wasm</span><span class="dl">'</span><span class="p">);</span>
    <span class="nt">&lt;/script&gt;</span>
  <span class="nt">&lt;/head&gt;</span>
  <span class="nt">&lt;body&gt;</span>
    <span class="nt">&lt;pre</span> <span class="na">id=</span><span class="s">"pre"</span><span class="nt">&gt;&lt;/pre&gt;</span>
  <span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<p>The build script:</p>

<p><code class="language-plaintext highlighter-rouge">build.sh</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#/bin/bash</span>

<span class="nb">mkdir</span> <span class="nt">-p</span> build
<span class="nb">cp </span>index.html build/
wasm-pack build <span class="nt">-t</span> no-modules <span class="nt">-d</span> build/
</code></pre></div></div>

<p>And we’re done. <code class="language-plaintext highlighter-rouge">wasm-pack</code> will do the dirty work for us!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./build.sh
</code></pre></div></div>

<p>Serve files from <code class="language-plaintext highlighter-rouge">build</code> folder, and use the browser.</p>

<p>Notice that the browser will give camera access when you’re using <code class="language-plaintext highlighter-rouge">https://</code> or <code class="language-plaintext highlighter-rouge">localhost</code>!</p>

<p>What is better? Rust or GO?</p>

<p>Creating the same application with Rust was much harder, but there are some positives because of that!
You’ll ask what? Harder is better? Yes! Rust compiler forced me to think deeper about what I am doing. Like a good, but strict friend ;)
Other than that the Rust runtime is a way faster.</p>

<p>For WASM I will choose Rust!</p>

<p>Don’t need to write whole thing yourself if you don’t want. Check out my <a href="https://github.com/fazibear/asciifyme.rust">github</a> or working an <a href="https://fazibear.me/asciifyme.rust/">app</a>.</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="rust" /><category term="wasm" /><category term="async" /><summary type="html"><![CDATA[We will take a look at WASM again! But this time with Rust. Why Rust? Because I can compile Rust directly into WASM.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/rusty-wasm.png" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/rusty-wasm.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Let’s GO WASM</title><link href="https://blog.fazibear.me/lets-go-wasm/" rel="alternate" type="text/html" title="Let’s GO WASM" /><published>2023-04-17T00:00:00+00:00</published><updated>2023-04-17T00:00:00+00:00</updated><id>https://blog.fazibear.me/lets-go-wasm</id><content type="html" xml:base="https://blog.fazibear.me/lets-go-wasm/"><![CDATA[<p>WASM? WebAssembly?</p>

<p>Lately I’ve started to ask myself: “Is WASM worth paying attention to?”</p>

<p>Let’s find out. There are few languages that can be directly compiled into WASM. Anyway let’s try GO.</p>

<p>We will make a simple web application that converts image from your webcam into ascii art.
The goal is to write as much code in Go as possible.</p>

<p>Let’s go!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># go mod init asciifyme</span>
</code></pre></div></div>

<p>And that’s it, everything works!</p>

<p>Just kidding. It’s not that simple.</p>

<p>We will need following pieces:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">webcam</code> - will initialize and fetch image from web cam</li>
  <li><code class="language-plaintext highlighter-rouge">canvas</code> - we need this to fetch pixel data from image</li>
  <li><code class="language-plaintext highlighter-rouge">asciifyier</code> - turn image data into string</li>
</ul>

<p>The webcam:</p>

<p>This module will:</p>
<ul>
  <li>create a <code class="language-plaintext highlighter-rouge">video</code> element with <code class="language-plaintext highlighter-rouge">document.createElement</code></li>
  <li>initialize webcam with <code class="language-plaintext highlighter-rouge">navigator.getUserMedia</code></li>
</ul>

<p>We need to create a file <code class="language-plaintext highlighter-rouge">webcam/webcam.go</code></p>

<p>First part looks like this:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">webcam</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"syscall/js"</span>
<span class="p">)</span>

<span class="k">var</span> <span class="p">(</span>
	<span class="n">navigator</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span>
	<span class="n">video</span>     <span class="n">js</span><span class="o">.</span><span class="n">Value</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">navigator</span> <span class="o">=</span> <span class="n">js</span><span class="o">.</span><span class="n">Global</span><span class="p">()</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"navigator"</span><span class="p">)</span>
	<span class="n">video</span> <span class="o">=</span> <span class="n">js</span><span class="o">.</span><span class="n">Global</span><span class="p">()</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"document"</span><span class="p">)</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"createElement"</span><span class="p">,</span> <span class="s">"video"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this code we are creating <code class="language-plaintext highlighter-rouge">video</code> element, and fetching <code class="language-plaintext highlighter-rouge">navigator</code> for future use.</p>

<p>It’s time to setup webcam:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">Setup</span><span class="p">()</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span> <span class="p">{</span>
	<span class="n">user_media_params</span> <span class="o">:=</span> <span class="k">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="k">interface</span><span class="p">{}{</span>
		<span class="s">"video"</span><span class="o">:</span> <span class="no">true</span><span class="p">,</span>
	<span class="p">}</span>

	<span class="n">navigator</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"getUserMedia"</span><span class="p">,</span> <span class="n">user_media_params</span><span class="p">,</span> <span class="n">js</span><span class="o">.</span><span class="n">FuncOf</span><span class="p">(</span><span class="n">stream</span><span class="p">),</span> <span class="n">js</span><span class="o">.</span><span class="n">FuncOf</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>

	<span class="k">return</span> <span class="n">video</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We will call this function from the main, it will setup webcam and return <code class="language-plaintext highlighter-rouge">video</code> object to fetch data from.
But wait! There are two callbacks <code class="language-plaintext highlighter-rouge">stream</code> and <code class="language-plaintext highlighter-rouge">err</code> we need to implement:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">err</span><span class="p">(</span><span class="n">this</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span> <span class="k">interface</span><span class="p">{}</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"err"</span><span class="p">)</span>
	<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">stream</span><span class="p">(</span><span class="n">this</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span> <span class="k">interface</span><span class="p">{}</span> <span class="p">{</span>
	<span class="n">video</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"srcObject"</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">])</span>
	<span class="n">video</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"addEventListener"</span><span class="p">,</span> <span class="s">"canplaythrough"</span><span class="p">,</span> <span class="n">js</span><span class="o">.</span><span class="n">FuncOf</span><span class="p">(</span><span class="n">canPlay</span><span class="p">))</span>
	<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For now we will ignore errors and write error on console.
<code class="language-plaintext highlighter-rouge">stream</code> function adds a stream to the video element and listen to <code class="language-plaintext highlighter-rouge">canplaythrough</code> event.
Another callback? Yes! <code class="language-plaintext highlighter-rouge">video</code> will call <code class="language-plaintext highlighter-rouge">canPlay</code> callback when there will be enough data.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">canPlay</span><span class="p">(</span><span class="n">this</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span> <span class="k">interface</span><span class="p">{}</span> <span class="p">{</span>
	<span class="n">video</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"play"</span><span class="p">)</span>
	<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When we have enough data press play!</p>

<p>We have a <code class="language-plaintext highlighter-rouge">video</code>, now we need a pixel data. Let’s create <code class="language-plaintext highlighter-rouge">canvas</code> in <code class="language-plaintext highlighter-rouge">canvas/canvas.go</code></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">canvas</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"syscall/js"</span>
<span class="p">)</span>

<span class="k">const</span> <span class="p">(</span>
	<span class="n">CanvasWidth</span>  <span class="o">=</span> <span class="m">80</span>
	<span class="n">CanvasHeight</span> <span class="o">=</span> <span class="m">40</span>
<span class="p">)</span>

<span class="k">var</span> <span class="p">(</span>
	<span class="n">ctx</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">ctx</span> <span class="o">=</span> <span class="n">js</span><span class="o">.</span><span class="n">Global</span><span class="p">()</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"document"</span><span class="p">)</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"createElement"</span><span class="p">,</span> <span class="s">"canvas"</span><span class="p">)</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"getContext"</span><span class="p">,</span> <span class="s">"2d"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We’re creating <code class="language-plaintext highlighter-rouge">canvas</code> element and fetching <code class="language-plaintext highlighter-rouge">context</code>. Will use it to draw and fetch pixel data.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">DrawImage</span><span class="p">(</span><span class="n">video</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">ctx</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"drawImage"</span><span class="p">,</span> <span class="n">video</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">CanvasWidth</span><span class="p">,</span> <span class="n">CanvasHeight</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can draw a frame from <code class="language-plaintext highlighter-rouge">video</code> by passing it into <code class="language-plaintext highlighter-rouge">drawImage</code> function.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">GetImageData</span><span class="p">()</span> <span class="p">[]</span><span class="kt">uint8</span> <span class="p">{</span>
	<span class="n">data</span> <span class="o">:=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"getImageData"</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">CanvasWidth</span><span class="p">,</span> <span class="n">CanvasHeight</span><span class="p">)</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"data"</span><span class="p">)</span>

	<span class="n">lenght</span> <span class="o">:=</span> <span class="n">data</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"length"</span><span class="p">)</span><span class="o">.</span><span class="n">Int</span><span class="p">()</span>

	<span class="n">goData</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">uint8</span><span class="p">,</span> <span class="n">lenght</span><span class="p">)</span>

	<span class="n">js</span><span class="o">.</span><span class="n">CopyBytesToGo</span><span class="p">(</span><span class="n">goData</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>

	<span class="k">return</span> <span class="n">goData</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Fetching pixel data is more complicated. We have to fetch JS array of <code class="language-plaintext highlighter-rouge">uint8</code> into GO.
This function takes the length of data from <code class="language-plaintext highlighter-rouge">canvas</code>, create GO array, and copy whole data into go array.
Voila! We have a pixel data.</p>

<p>What’s left? Convert it to asciiart.</p>

<p><code class="language-plaintext highlighter-rouge">asciifyier/asciifyier.go</code> is what we need!</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">asciifyier</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"asciifyme/canvas"</span>
<span class="p">)</span>

<span class="k">const</span> <span class="p">(</span>
	<span class="n">Chars</span>       <span class="o">=</span> <span class="s">"   .,:;i1tfLCG08@"</span>
	<span class="n">CharsLength</span> <span class="o">=</span> <span class="m">16</span>
<span class="p">)</span>
</code></pre></div></div>

<p>We don’t need any JS stuff here. But need to import our <code class="language-plaintext highlighter-rouge">canvas</code> to fetch its size.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">Asciify</span><span class="p">(</span><span class="n">data</span> <span class="p">[]</span><span class="kt">uint8</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
	<span class="n">output</span> <span class="o">:=</span> <span class="s">""</span>

	<span class="k">for</span> <span class="n">y</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">canvas</span><span class="o">.</span><span class="n">CanvasHeight</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span> <span class="p">{</span>
		<span class="k">for</span> <span class="n">x</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">canvas</span><span class="o">.</span><span class="n">CanvasWidth</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span> <span class="p">{</span>
			<span class="n">offset</span> <span class="o">:=</span> <span class="p">(</span><span class="n">y</span><span class="o">*</span><span class="n">canvas</span><span class="o">.</span><span class="n">CanvasWidth</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="m">4</span>

			<span class="n">red</span> <span class="o">:=</span> <span class="n">data</span><span class="p">[</span><span class="n">offset</span><span class="p">]</span>
			<span class="n">green</span> <span class="o">:=</span> <span class="n">data</span><span class="p">[</span><span class="n">offset</span><span class="o">+</span><span class="m">1</span><span class="p">]</span>
			<span class="n">blue</span> <span class="o">:=</span> <span class="n">data</span><span class="p">[</span><span class="n">offset</span><span class="o">+</span><span class="m">2</span><span class="p">]</span>
			<span class="c">//alpha := data[offset+3]</span>

			<span class="n">brightness</span> <span class="o">:=</span> <span class="p">(</span><span class="m">0.3</span><span class="o">*</span><span class="kt">float64</span><span class="p">(</span><span class="n">red</span><span class="p">)</span> <span class="o">+</span> <span class="m">0.59</span><span class="o">*</span><span class="kt">float64</span><span class="p">(</span><span class="n">green</span><span class="p">)</span> <span class="o">+</span> <span class="m">0.11</span><span class="o">*</span><span class="kt">float64</span><span class="p">(</span><span class="n">blue</span><span class="p">))</span> <span class="o">/</span> <span class="m">255.0</span>

			<span class="n">char_index</span> <span class="o">:=</span> <span class="n">CharsLength</span> <span class="o">-</span> <span class="kt">int</span><span class="p">(</span><span class="n">brightness</span><span class="o">*</span><span class="n">CharsLength</span><span class="p">)</span>

			<span class="n">output</span> <span class="o">+=</span> <span class="kt">string</span><span class="p">(</span><span class="n">Chars</span><span class="p">[</span><span class="n">char_index</span><span class="p">])</span>
		<span class="p">}</span>
		<span class="n">output</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="n">output</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What we’re doing here? We’re taking each pixel data from array of <code class="language-plaintext highlighter-rouge">uint8</code> and creating a string. Our asciiart.</p>

<p>It’s time for <code class="language-plaintext highlighter-rouge">main.go</code> …</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"asciifyme/asciifyier"</span>
	<span class="s">"asciifyme/canvas"</span>
	<span class="s">"asciifyme/webcam"</span>
	<span class="s">"syscall/js"</span>
<span class="p">)</span>

<span class="k">var</span> <span class="p">(</span>
	<span class="n">camera</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span>
	<span class="n">window</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span>
	<span class="n">pre</span>    <span class="n">js</span><span class="o">.</span><span class="n">Value</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">camera</span> <span class="o">=</span> <span class="n">webcam</span><span class="o">.</span><span class="n">Setup</span><span class="p">()</span>
	<span class="n">window</span> <span class="o">=</span> <span class="n">js</span><span class="o">.</span><span class="n">Global</span><span class="p">()</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"window"</span><span class="p">)</span>
	<span class="n">pre</span> <span class="o">=</span> <span class="n">js</span><span class="o">.</span><span class="n">Global</span><span class="p">()</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"document"</span><span class="p">)</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"getElementById"</span><span class="p">,</span> <span class="s">"pre"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Taking all the pieces together. We will need a <code class="language-plaintext highlighter-rouge">camera</code>, <code class="language-plaintext highlighter-rouge">window.requestAnimationFrame</code>, and <code class="language-plaintext highlighter-rouge">pre</code> element to display our asciiart.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">loop</span><span class="p">(</span><span class="n">this</span> <span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span> <span class="k">interface</span><span class="p">{}</span> <span class="p">{</span>
	<span class="n">window</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="s">"requestAnimationFrame"</span><span class="p">,</span> <span class="n">js</span><span class="o">.</span><span class="n">FuncOf</span><span class="p">(</span><span class="n">loop</span><span class="p">))</span>
	<span class="n">canvas</span><span class="o">.</span><span class="n">DrawImage</span><span class="p">(</span><span class="n">camera</span><span class="p">)</span>
	<span class="n">imageData</span> <span class="o">:=</span> <span class="n">canvas</span><span class="o">.</span><span class="n">GetImageData</span><span class="p">()</span>
	<span class="n">output</span> <span class="o">:=</span> <span class="n">asciifyier</span><span class="o">.</span><span class="n">Asciify</span><span class="p">(</span><span class="n">imageData</span><span class="p">)</span>
	<span class="n">pre</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"innerHTML"</span><span class="p">,</span> <span class="n">output</span><span class="p">)</span>
	<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">loop</span><span class="p">(</span><span class="n">js</span><span class="o">.</span><span class="n">ValueOf</span><span class="p">(</span><span class="no">nil</span><span class="p">),</span> <span class="nb">make</span><span class="p">([]</span><span class="n">js</span><span class="o">.</span><span class="n">Value</span><span class="p">,</span> <span class="m">0</span><span class="p">))</span>

	<span class="k">select</span> <span class="p">{}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In main loop we’re:</p>
<ul>
  <li>fetching data from <code class="language-plaintext highlighter-rouge">video</code></li>
  <li>drawing it on <code class="language-plaintext highlighter-rouge">canvas</code></li>
  <li>fetch pixel data from <code class="language-plaintext highlighter-rouge">canvas</code></li>
  <li>create asciiart using <code class="language-plaintext highlighter-rouge">asciifyier</code></li>
  <li>draw asciify into <code class="language-plaintext highlighter-rouge">pre</code></li>
</ul>

<p>One more thing! <code class="language-plaintext highlighter-rouge">select {}</code> make the wasm program don’t quit!</p>

<p>That’s it. Compile time!</p>

<p>To run this in the browser we need:</p>
<ul>
  <li>index.html</li>
  <li>wasm_exec.js</li>
  <li>compiled app</li>
</ul>

<p>Simple <code class="language-plaintext highlighter-rouge">index.html</code> file</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;html&gt;</span>
  <span class="nt">&lt;head&gt;</span>
    <span class="nt">&lt;title&gt;</span>asscify-me<span class="nt">&lt;/title&gt;</span>
    <span class="nt">&lt;style&gt;</span>
      <span class="nt">body</span><span class="p">{</span><span class="nl">background-color</span><span class="p">:</span><span class="nx">#000</span><span class="p">}</span><span class="nt">pre</span><span class="p">{</span><span class="nl">text-align</span><span class="p">:</span><span class="nb">center</span><span class="p">}</span><span class="nt">header</span><span class="p">{</span><span class="nl">color</span><span class="p">:</span><span class="nx">#daa520</span><span class="p">;</span><span class="nl">font-size</span><span class="p">:</span><span class="m">18px</span><span class="p">;</span><span class="nl">font-weight</span><span class="p">:</span><span class="m">700</span><span class="p">;</span><span class="nl">text-shadow</span><span class="p">:</span><span class="m">0</span> <span class="m">0</span> <span class="m">3px</span> <span class="nx">gold</span><span class="p">}</span><span class="nt">section</span><span class="p">{</span><span class="nl">margin-top</span><span class="p">:</span><span class="m">30px</span><span class="p">;</span><span class="nl">color</span><span class="p">:</span><span class="nx">#32cd32</span><span class="p">;</span><span class="nl">text-shadow</span><span class="p">:</span><span class="m">0</span> <span class="m">0</span> <span class="m">15px</span> <span class="nx">#0f0</span><span class="p">;</span><span class="nl">font-size</span><span class="p">:</span><span class="m">14px</span><span class="p">}</span><span class="nt">footer</span><span class="o">,</span><span class="nt">footer</span> <span class="nt">a</span><span class="p">{</span><span class="nl">margin-top</span><span class="p">:</span><span class="m">30px</span><span class="p">;</span><span class="nl">color</span><span class="p">:</span><span class="nx">red</span><span class="p">;</span><span class="nl">text-shadow</span><span class="p">:</span><span class="m">0</span> <span class="m">0</span> <span class="m">15px</span> <span class="nx">tomato</span><span class="p">;</span><span class="nl">font-size</span><span class="p">:</span><span class="m">14px</span><span class="p">}</span>
    <span class="nt">&lt;/style&gt;</span>
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"wasm_exec.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;script&gt;</span>
    <span class="kd">const</span> <span class="nx">go</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Go</span><span class="p">();</span>
    <span class="nx">WebAssembly</span><span class="p">.</span><span class="nf">instantiateStreaming</span><span class="p">(</span><span class="nf">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">asciifyme.wasm</span><span class="dl">"</span><span class="p">),</span> <span class="nx">go</span><span class="p">.</span><span class="nx">importObject</span><span class="p">).</span><span class="nf">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">go</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">instance</span><span class="p">);</span>
    <span class="p">});</span>
    <span class="nt">&lt;/script&gt;</span>
  <span class="nt">&lt;/head&gt;</span>
  <span class="nt">&lt;body&gt;</span>
    <span class="nt">&lt;pre</span> <span class="na">id=</span><span class="s">"pre"</span><span class="nt">&gt;&lt;/pre&gt;</span>
  <span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<p>And the build script:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#/bin/bash</span>

<span class="nb">export </span><span class="nv">GOOS</span><span class="o">=</span>js
<span class="nb">export </span><span class="nv">GOARCH</span><span class="o">=</span>wasm
<span class="nb">mkdir</span> <span class="nt">-p</span> build
<span class="nb">cp </span>index.html build/
<span class="nb">cp</span> <span class="s2">"</span><span class="si">$(</span>go <span class="nb">env </span>GOROOT<span class="si">)</span><span class="s2">/misc/wasm/wasm_exec.js"</span> build/
go build <span class="nt">-o</span> build/asciifyme.wasm
</code></pre></div></div>

<p>Now we need to run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ./build.sh</span>
</code></pre></div></div>

<p>Serve files from <code class="language-plaintext highlighter-rouge">build</code> folder, and use the browser.</p>

<p>Notice that the browser will give camera access when you’re using <code class="language-plaintext highlighter-rouge">https://</code> or <code class="language-plaintext highlighter-rouge">localhost</code></p>

<p>P.S</p>

<p>But wait! The size! ~2MB is way to much! Yes!</p>

<p>Try <code class="language-plaintext highlighter-rouge">thinygo</code> compiler, ~200KB is much better!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># tinygo build -o build/asciifyme.wasm -target wasm</span>
</code></pre></div></div>

<p>Don’t need to write whole thing yourself if you don’t want. Check out my <a href="https://github.com/fazibear/asciifyme.go">github</a> or working an <a href="https://fazibear.me/asciifyme.go/">app</a>.</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="go" /><category term="wasm" /><summary type="html"><![CDATA[WASM? WebAssembly?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/go-wasm.png" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/go-wasm.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Best dotfiles manager is GIT</title><link href="https://blog.fazibear.me/best-dotfiles-manager-is-git/" rel="alternate" type="text/html" title="Best dotfiles manager is GIT" /><published>2023-03-20T00:00:00+00:00</published><updated>2023-03-20T00:00:00+00:00</updated><id>https://blog.fazibear.me/best-dotfiles-manager-is-git</id><content type="html" xml:base="https://blog.fazibear.me/best-dotfiles-manager-is-git/"><![CDATA[<p>Long story short. Tried lot of dotfiles managers including custom solutions. All of them have a big disadvantage. You have a copy of a file or symlink.
What do we need? Just git. Let’s start!</p>

<p>First of all we need a shell alias to manage our files.</p>

<p>I’m using fish so creating alias looks like this.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">alias </span>hi <span class="s2">"echo hello"</span>
</code></pre></div></div>

<p>But you can do same thing in bash as well:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">alias </span><span class="nv">hi</span><span class="o">=</span><span class="s2">"echo hello"</span>
</code></pre></div></div>

<p>Let’s name our alias <code class="language-plaintext highlighter-rouge">dot</code>.</p>

<p>Just add such a line in your shell config file.</p>

<p>Depending on our needs we can:</p>

<p>Allow to backup files on whole disk:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span>dot <span class="s2">"git --git-dir=</span><span class="nv">$HOME</span><span class="s2">/.dotfiles --work-tree=/"</span>
</code></pre></div></div>

<p>Or just in the home directory:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span>dot <span class="s2">"git --git-dir=</span><span class="nv">$HOME</span><span class="s2">/.dotfiles --work-tree=</span><span class="nv">$HOME</span><span class="s2">"</span>
</code></pre></div></div>

<p>Great. We’re just made an dot manager!</p>

<p>Let’s init out repo</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dot init
</code></pre></div></div>

<p>One more thing! Need to set up one thing:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dot config <span class="nt">--local</span> status.showUntrackedFiles no
</code></pre></div></div>

<p>And we’re done. All git files are in <code class="language-plaintext highlighter-rouge">$HOME/.dotfiles</code> directory.</p>

<p>To add a file or directory we can simply:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dot add ~/.config
</code></pre></div></div>

<p>and commit it with</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dot commit
</code></pre></div></div>

<p>The backup is a <code class="language-plaintext highlighter-rouge">git</code> repository. We can push it to github for example. Just add remote like on every other git repository.</p>

<p>Just like you saw. There is no copying, linking, just adding a files when something changed.</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="git" /><category term="symlink" /><category term="dotfiles" /><summary type="html"><![CDATA[Long story short. Tried lot of dotfiles managers including custom solutions. All of them have a big disadvantage. You have a copy of a file or symlink. What do we need? Just git. Let’s start!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/symlink.jpg" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/symlink.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to Create Desktop Application with Elixir</title><link href="https://blog.fazibear.me/how-to-create-desktop-application-with-elixir/" rel="alternate" type="text/html" title="How to Create Desktop Application with Elixir" /><published>2019-09-07T00:00:00+00:00</published><updated>2019-09-07T00:00:00+00:00</updated><id>https://blog.fazibear.me/how-to-create-desktop-application-with-elixir</id><content type="html" xml:base="https://blog.fazibear.me/how-to-create-desktop-application-with-elixir/"><![CDATA[<p>Writing desktop application is not an easy task. But why don’t use Elixir, Phoenix and LiveView?
We will figure out how we can create a desktop application using those technologies.</p>

<h2 id="phoenix-framework">Phoenix Framework</h2>

<p>We will use the Phoenix framework. All the details on how to install phoenix can be found here: https://hexdocs.pm/phoenix/installation.html</p>

<p>Let’s get started!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix phx.new elixir_desktop_application <span class="nt">--no-ecto</span>
<span class="nb">cd </span>elixir_desktop_application
</code></pre></div></div>

<h2 id="phoenix-liveview">Phoenix LiveView</h2>

<p>So far so good. To make things hot we will use <a href="https://github.com/phoenixframework/phoenix_live_view">LiveView</a>. Here is the <a href="https://github.com/phoenixframework/phoenix_live_view#installation">installation guide</a>. After settings up Phoenix and LiveView, we will push things to git.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git init <span class="nb">.</span>
git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"Initial commit"</span>
</code></pre></div></div>

<p>Moving further We’ll a build simple file browser. Out root LiveView will look like this:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">scope</span> <span class="s2">"/"</span><span class="p">,</span> <span class="no">ElixirDesktopApplicationWeb</span> <span class="k">do</span>
  <span class="n">pipe_through</span> <span class="ss">:browser</span>

  <span class="n">live</span> <span class="s2">"/"</span><span class="p">,</span> <span class="no">FileBrowserLive</span>
<span class="k">end</span>
</code></pre></div></div>

<p>And write a simple file browser live view:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">ElixirDesktopApplicationWeb</span><span class="o">.</span><span class="no">FileBrowserLive</span> <span class="k">do</span>
  <span class="kn">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="no">LiveView</span>

  <span class="k">def</span> <span class="n">render</span><span class="p">(</span><span class="n">assigns</span><span class="p">)</span> <span class="k">do</span>
    <span class="sx">~L""</span><span class="s2">"
    &lt;h3&gt;&lt;%= @current %&gt;&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li phx-click="</span><span class="n">cd</span><span class="s2">" phx-value="</span><span class="o">..</span><span class="s2">"&gt;&lt;b&gt;..&lt;/b&gt;&lt;/li&gt;
      &lt;%= for entry &lt;- @dirs do %&gt;
        &lt;li phx-click="</span><span class="n">cd</span><span class="s2">" phx-value="</span><span class="o">&lt;%=</span> <span class="n">entry</span> <span class="p">%</span><span class="o">&gt;</span><span class="s2">"&gt;&lt;b&gt;&lt;%= entry %&gt;&lt;/b&gt;&lt;/li&gt;
      &lt;% end %&gt;
      &lt;%= for entry &lt;- @files do %&gt;
        &lt;li&gt;&lt;%= entry %&gt;&lt;/li&gt;
      &lt;% end %&gt;
    &lt;/ul&gt;
    """</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">mount</span><span class="p">(</span><span class="n">_session</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">socket</span> <span class="o">=</span>
      <span class="n">socket</span>
      <span class="o">|&gt;</span> <span class="n">assign</span><span class="p">(</span><span class="ss">current:</span> <span class="no">Path</span><span class="o">.</span><span class="n">expand</span><span class="p">(</span><span class="s2">"."</span><span class="p">))</span>
      <span class="o">|&gt;</span> <span class="n">ls</span><span class="p">()</span>

    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">socket</span><span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">handle_event</span><span class="p">(</span><span class="s2">"cd"</span><span class="p">,</span> <span class="n">param</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">socket</span> <span class="o">=</span>
      <span class="n">socket</span>
      <span class="o">|&gt;</span> <span class="n">assign</span><span class="p">(</span><span class="ss">current:</span> <span class="n">path</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="n">param</span><span class="p">))</span>
      <span class="o">|&gt;</span> <span class="n">ls</span><span class="p">()</span>

    <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span><span class="p">}</span>
  <span class="k">end</span>

  <span class="k">defp</span> <span class="n">ls</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
    <span class="k">case</span> <span class="no">File</span><span class="o">.</span><span class="n">ls</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">assigns</span><span class="o">.</span><span class="n">current</span><span class="p">)</span> <span class="k">do</span>
      <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">entries</span><span class="p">}</span> <span class="o">-&gt;</span>
        <span class="p">{</span><span class="n">dirs</span><span class="p">,</span> <span class="n">files</span><span class="p">}</span> <span class="o">=</span> <span class="no">Enum</span><span class="o">.</span><span class="n">split_with</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="o">&amp;</span><span class="no">File</span><span class="o">.</span><span class="n">dir?</span><span class="p">(</span><span class="n">path</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="nv">&amp;1</span><span class="p">)))</span>

        <span class="n">assign</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="ss">dirs:</span> <span class="no">Enum</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">dirs</span><span class="p">),</span> <span class="ss">files:</span> <span class="no">Enum</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">files</span><span class="p">))</span>

      <span class="n">_</span> <span class="o">-&gt;</span>
        <span class="n">socket</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">defp</span> <span class="n">path</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="n">param</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">Path</span><span class="o">.</span><span class="n">expand</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">assigns</span><span class="o">.</span><span class="n">current</span> <span class="o">&lt;&gt;</span> <span class="s2">"/"</span> <span class="o">&lt;&gt;</span> <span class="n">param</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Take a look at http://localhost:4000/ such a nice file browser huh?</p>

<h2 id="webenginekiosk">WebEngineKiosk</h2>

<p>To make our app desktop we need a window. What we can do about it? There is something we can use <a href="https://github.com/fhunleth/webengine_kiosk">webengine_kioks</a>. Let use it!</p>

<p>We need to add it as dependency:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="ss">:webengine_kiosk</span><span class="p">,</span> <span class="s2">"~&gt; 0.2"</span><span class="p">}</span>
</code></pre></div></div>

<p>And insert it to our supervision tree:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">ElixirDesktopApplication</span><span class="o">.</span><span class="no">Application</span> <span class="k">do</span>
  <span class="o">...</span>
  <span class="k">def</span> <span class="n">start</span><span class="p">(</span><span class="n">_type</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
    <span class="c1"># List all child processes to be supervised</span>
    <span class="n">children</span> <span class="o">=</span> <span class="p">[</span>
      <span class="c1"># Start the endpoint when the application starts</span>
      <span class="no">ElixirDesktopApplicationWeb</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
      <span class="c1"># Starts a worker by calling: ElixirDesktopApplication.Worker.start_link(arg)</span>
      <span class="c1"># {ElixirDesktopApplication.Worker, arg},</span>
      <span class="p">{</span><span class="no">WebengineKiosk</span><span class="p">,</span> <span class="p">{[</span><span class="ss">homepage:</span> <span class="s2">"http://localhost:4000"</span><span class="p">,</span> <span class="ss">fullscreen:</span> <span class="no">false</span><span class="p">],</span> <span class="ss">name:</span> <span class="no">MyKiosk</span><span class="p">}}</span>
    <span class="p">]</span>

    <span class="c1"># See https://hexdocs.pm/elixir/Supervisor.html</span>
    <span class="c1"># for other strategies and supported options</span>
    <span class="n">opts</span> <span class="o">=</span> <span class="p">[</span><span class="ss">strategy:</span> <span class="ss">:one_for_one</span><span class="p">,</span> <span class="ss">name:</span> <span class="no">ElixirDesktopApplication</span><span class="o">.</span><span class="no">Supervisor</span><span class="p">]</span>
    <span class="no">Supervisor</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">children</span><span class="p">,</span> <span class="n">opts</span><span class="p">)</span>
  <span class="k">end</span>
  <span class="o">...</span>
<span class="k">end</span>

</code></pre></div></div>

<p>Easy peasy! Is it works? Let start our application:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix phx.server
</code></pre></div></div>

<p>Wow! The window appeared with our browser. Nice! But we can go further! The erlang release.</p>

<h2 id="i-cant-quit">I Can’t Quit!</h2>

<p>But wait! I can’t quit! Hit <code class="language-plaintext highlighter-rouge">ctrl-c</code> in your terminal and add the quit button:</p>

<p>In <code class="language-plaintext highlighter-rouge">lib/elixir_desktop_application_web/live/file_browser_live.ex</code> :</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">def</span> <span class="n">render</span><span class="p">(</span><span class="n">assigns</span><span class="p">)</span> <span class="k">do</span>
    <span class="sx">~L""</span><span class="s2">"
    &lt;h4 phx-click="</span><span class="n">quit</span><span class="s2">"&gt;quit&lt;/h4&gt;
    """</span>
    <span class="o">...</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">handle_event</span><span class="p">(</span><span class="s2">"quit"</span><span class="p">,</span> <span class="n">_param</span><span class="p">,</span> <span class="n">_socket</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
  <span class="k">end</span>
</code></pre></div></div>

<p>We can close application now.</p>

<h2 id="otp-release">OTP Release</h2>

<p>OTP releases are included in elixir 1.9.
Out build script can look like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nb">export </span><span class="nv">SECRET_KEY_BASE</span><span class="o">=</span>ENhzfdb/3IwgO8LX0QHfYqPfpU6I8kyrPG348vFwRkzxG0CjN7+egBO/F0RJnjxE
<span class="nb">export </span><span class="nv">MIX_ENV</span><span class="o">=</span>prod

mix deps.get <span class="nt">--only</span> prod
mix compile
npm run deploy <span class="nt">--prefix</span> ./assets
mix phx.digest
mix release <span class="nt">--overwrite</span>
</code></pre></div></div>

<p>And few more tweaks to our application. In<code class="language-plaintext highlighter-rouge">config/prod.exs</code>:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span> <span class="ss">:elixir_desktop_application</span><span class="p">,</span> <span class="no">ElixirDesktopApplicationWeb</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
  <span class="ss">cache_static_manifest:</span> <span class="s2">"priv/static/cache_manifest.json"</span><span class="p">,</span>
  <span class="ss">server:</span> <span class="no">true</span>
</code></pre></div></div>

<p>Finally, create the build!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./build.sh
</code></pre></div></div>

<p>And run!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>_build/prod/rel/elixir_desktop_application/bin/elixir_desktop_application start
</code></pre></div></div>

<p>Success!</p>

<h2 id="osx-application">OSX Application</h2>

<p>Yay! Everything still works. But this is not an app, right? Right. We will try to create an OSX app…</p>

<p>Updated build script looks like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nb">export </span><span class="nv">SECRET_KEY_BASE</span><span class="o">=</span>ENhzfdb/3IwgO8LX0QHfYqPfpU6I8kyrPG348vFwRkzxG0CjN7+egBO/F0RJnjxE
<span class="nb">export </span><span class="nv">MIX_ENV</span><span class="o">=</span>prod

mix deps.get <span class="nt">--only</span> prod
mix compile
npm run deploy <span class="nt">--prefix</span> ./assets
mix phx.digest
mix release <span class="nt">--overwrite</span>

<span class="nv">APP</span><span class="o">=</span>FileBrowser
<span class="nv">APP_DIR</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">APP</span><span class="k">}</span><span class="s2">.app/Contents/MacOS"</span>
<span class="nv">RELEASE</span><span class="o">=</span>_build/prod/rel/elixir_desktop_application

<span class="nb">rm</span> <span class="nt">-rf</span> <span class="nv">$APP</span>.app
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$APP_DIR</span>
<span class="nb">echo</span> <span class="s2">"cp -r </span><span class="nv">$RELEASE</span><span class="s2"> </span><span class="nv">$APP_DIR</span><span class="s2">"</span>
<span class="nb">cp</span> <span class="nt">-r</span> <span class="nv">$RELEASE</span> <span class="nv">$APP_DIR</span>
<span class="nb">echo</span> <span class="s2">"#!/bin/bash"</span> <span class="o">&gt;</span> <span class="nv">$APP_DIR</span>/<span class="nv">$APP</span>
<span class="nb">echo</span> <span class="s1">'DIR="\$( cd "$( dirname "${BASH_SOURCE[0]}" )" &gt;/dev/null 2&gt;&amp;1 &amp;&amp; pwd )'</span> <span class="o">&gt;&gt;</span> <span class="nv">$APP_DIR</span>/<span class="nv">$APP</span>
<span class="nb">echo</span> <span class="s1">'$DIR/elixir_desktop_application/bin/elixir_desktop_application start'</span> <span class="o">&gt;&gt;</span> <span class="nv">$APP_DIR</span>/<span class="nv">$APP</span>
<span class="nb">chmod</span> +x <span class="nv">$APP_DIR</span>/<span class="nv">$APP</span>
</code></pre></div></div>

<p>Just run the script, click on FileBrowser.app … and … voila! But…</p>

<h2 id="not-so-fast">Not So Fast!</h2>
<p>Although it all works just fine it’s not ready to distribute! There are a few things we need to look at!</p>

<h4 id="http">HTTP</h4>
<p>We’re still communicating with HTTP. The browser talks to the server over HTTP. That’s bad. You can’t run a few instances at the same time. Is there a solution? Yes, it is! We can communicate over a port instead of HTTP. With the phoenix framework, it can be done. Also, some changes to WebEngineKiosk are required.</p>

<h4 id="qt5-libraries">QT5 Libraries</h4>
<p>This application will work fine on your computer, but not on another. QT5 libraries need to be installed. We can include those libraries in our bundle and relink the binary.</p>

<h4 id="other-platforms">Other platforms</h4>

<p>What about other platforms? Erlang releases works on Windows and Linux. It shouldn’t be hard to make create releases for those platforms as well.</p>

<h2 id="its-possible">It’s possible!</h2>

<p>This is just proof of concept. But it turns out that we are not far away from the framework like Electron for Elixir. Will you be interested in such a thing? Are you interested in writing the desktop application in Elixir with a little bit of Javascript? What do you think?</p>

<p>P.S. The source code is here: https://github.com/fazibear/elixir_desktop_application</p>]]></content><author><name>Michał Kalbarczyk</name></author><category term="elixir" /><category term="phoenix" /><category term="live view" /><category term="chromium" /><category term="electron" /><summary type="html"><![CDATA[Writing desktop application is not an easy task. But why don’t use Elixir, Phoenix and LiveView? We will figure out how we can create a desktop application using those technologies.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/desktop-app.jpg" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/desktop-app.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to Use Elixir’s Compiler to Avoid Typos</title><link href="https://blog.fazibear.me/how-to-use-elixir-s-compiler-to-avoid-typos/" rel="alternate" type="text/html" title="How to Use Elixir’s Compiler to Avoid Typos" /><published>2019-08-23T00:00:00+00:00</published><updated>2019-08-23T00:00:00+00:00</updated><id>https://blog.fazibear.me/how-to-use-elixir-s-compiler-to-avoid-typos</id><content type="html" xml:base="https://blog.fazibear.me/how-to-use-elixir-s-compiler-to-avoid-typos/"><![CDATA[<p>In this article, I want to describe how you can use elixir metaprogramming to avoid some runtime errors caused by typos. If you’re passing around some handcrafted messages ex. <code class="language-plaintext highlighter-rouge">%{name: "app_one_hello", payload: "payload"}</code> you can easily introduce errors. Sending <code class="language-plaintext highlighter-rouge">app_one_hello</code> and listening for <code class="language-plaintext highlighter-rouge">app_ane_hello</code> will not make your system works correctly. Imagine you have thousands of messages. It’s really hard to manage them like that. To make things better, you can create a list of all messages and generate functions to send and receive them. Now when you will create a typo, elixir’s compiler will tell you!</p>

<h2 id="example-application">Example application</h2>

<p>Under the umbrella we have 3 apps.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">dispatcher</code></li>
  <li><code class="language-plaintext highlighter-rouge">app_one</code></li>
  <li><code class="language-plaintext highlighter-rouge">app_two</code></li>
</ul>

<p><code class="language-plaintext highlighter-rouge">app_one</code> and <code class="language-plaintext highlighter-rouge">app_two</code> will send and receive messages managed by the <code class="language-plaintext highlighter-rouge">dispatcher</code></p>

<h3 id="the-dispatcher">The Dispatcher</h3>

<p>This it the application we will be working on. This is the main module:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Dispatcher</span> <span class="k">do</span>
  <span class="nv">@moduledoc</span> <span class="sd">"""
  Dispatcher is for dispatching messages.
  """</span>

  <span class="kn">use</span> <span class="no">GenStage</span>
  <span class="kn">require</span> <span class="no">Logger</span>

  <span class="k">def</span> <span class="n">start_link</span><span class="p">(</span><span class="n">_opts</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">GenStage</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="bp">__MODULE__</span><span class="p">,</span> <span class="p">[],</span> <span class="ss">name:</span> <span class="bp">__MODULE__</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">message</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">GenStage</span><span class="o">.</span><span class="n">cast</span><span class="p">(</span><span class="bp">__MODULE__</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">init</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">do</span>
    <span class="p">{</span><span class="ss">:producer</span><span class="p">,</span> <span class="p">%{</span><span class="ss">enabled:</span> <span class="no">true</span><span class="p">},</span> <span class="ss">dispatcher:</span> <span class="no">GenStage</span><span class="o">.</span><span class="no">BroadcastDispatcher</span><span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">handle_info</span><span class="p">(</span><span class="ss">:disable</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
    <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="p">[],</span> <span class="p">%{</span><span class="n">state</span> <span class="o">|</span> <span class="ss">enabled:</span> <span class="no">false</span><span class="p">}}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">handle_info</span><span class="p">(</span><span class="ss">:enable</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
    <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="p">[],</span> <span class="p">%{</span><span class="n">state</span> <span class="o">|</span> <span class="ss">enabled:</span> <span class="no">true</span><span class="p">}}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">handle_cast</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="p">%{</span><span class="ss">enabled:</span> <span class="no">true</span><span class="p">}</span> <span class="o">=</span> <span class="n">state</span><span class="p">)</span> <span class="ow">when</span> <span class="n">is_map</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">Logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">"Dispatch message: </span><span class="si">#{</span><span class="n">inspect</span><span class="p">(</span><span class="n">message</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="p">[</span><span class="n">message</span><span class="p">],</span> <span class="n">state</span><span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">handle_demand</span><span class="p">(</span><span class="n">_demand</span><span class="p">,</span> <span class="n">state</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="p">[],</span> <span class="n">state</span><span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This is simple pubsub using <code class="language-plaintext highlighter-rouge">gen_stage</code>. The message will be send to any subscribed processes.</p>

<p>To make it easier to manage each app, there is also a <code class="language-plaintext highlighter-rouge">Listener</code></p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Listener</span> <span class="k">do</span>
  <span class="nv">@moduledoc</span> <span class="sd">"""
  Listener for messages from dispatcher
  """</span>
  <span class="nv">@callback</span> <span class="n">on_message</span><span class="p">(</span><span class="n">message</span> <span class="p">::</span> <span class="n">map</span><span class="p">)</span> <span class="p">::</span> <span class="n">any</span>

  <span class="nv">@doc</span> <span class="no">false</span>
  <span class="k">defmacro</span> <span class="n">__using__</span><span class="p">(</span><span class="n">_opts</span><span class="p">)</span> <span class="k">do</span>
    <span class="kn">quote</span> <span class="ss">location:</span> <span class="ss">:keep</span> <span class="k">do</span>
      <span class="nv">@behaviour</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Listener</span>

      <span class="kn">use</span> <span class="no">GenStage</span>

      <span class="k">def</span> <span class="n">start_link</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span> <span class="k">do</span>
        <span class="no">GenStage</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="bp">__MODULE__</span><span class="p">,</span> <span class="n">opts</span><span class="p">,</span> <span class="ss">name:</span> <span class="bp">__MODULE__</span><span class="p">)</span>
      <span class="k">end</span>

      <span class="k">def</span> <span class="n">init</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="k">do</span>
        <span class="p">{</span><span class="ss">:consumer</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="ss">subscribe_to:</span> <span class="p">[</span><span class="no">Dispatcher</span><span class="p">]}</span>
      <span class="k">end</span>

      <span class="k">def</span> <span class="n">handle_events</span><span class="p">(</span><span class="n">messages</span><span class="p">,</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
        <span class="n">for</span> <span class="n">message</span> <span class="o">&lt;-</span> <span class="n">messages</span> <span class="k">do</span>
          <span class="n">on_message</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
        <span class="k">end</span>

        <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="p">[],</span> <span class="n">state</span><span class="p">}</span>
      <span class="k">end</span>

      <span class="k">def</span> <span class="n">on_message</span><span class="p">(</span><span class="n">_</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:nothing</span>

      <span class="n">defoverridable</span> <span class="ss">on_message:</span> <span class="mi">1</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This module makes easier to listen to messages.</p>

<p>Now you can send message like this:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Dispatcher</span><span class="o">.</span><span class="n">message</span><span class="p">(%{</span><span class="ss">name:</span> <span class="s2">"app_one_hello"</span><span class="p">,</span> <span class="ss">payload:</span> <span class="s2">"some_kind_of_payload"</span><span class="p">}</span>
</code></pre></div></div>

<p>And receive it with a little module:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">AppOne</span><span class="o">.</span><span class="no">Listener</span> <span class="k">do</span>
  <span class="nv">@moduledoc</span> <span class="sd">"""
  AppOne listener
  """</span>
  <span class="kn">use</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Listener</span>

  <span class="k">def</span> <span class="n">on_message</span><span class="p">(%{</span><span class="ss">name:</span> <span class="s2">"app_one_hello"</span><span class="p">,</span> <span class="ss">payload:</span> <span class="n">payload</span><span class="p">})</span> <span class="k">do</span>
    <span class="no">AppOne</span><span class="o">.</span><span class="n">hello</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="n">on_message</span><span class="p">(</span><span class="n">_</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:nothing</span>
<span class="k">end</span>

</code></pre></div></div>

<p>It works fine, but you can easily type <code class="language-plaintext highlighter-rouge">app_twe_hello</code> instead of <code class="language-plaintext highlighter-rouge">app_two_hello</code>. Also when you have thousands of messages you need to search the codebase for names. It might be difficult.</p>

<p>Why not use the compiler to track such errors?</p>

<h3 id="metaprogramming">Metaprogramming</h3>

<p>We will use an elixir’s neat feature for that!. Metaprogramming.</p>

<p>First we need a module to store all of the messages:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Message</span> <span class="k">do</span>
  <span class="nv">@moduledoc</span> <span class="sd">"""
  List of all messages
  """</span>

  <span class="nv">@list</span> <span class="sx">~w[
      app_one_hello
      app_two_hello
    ]a</span>

  <span class="k">def</span> <span class="n">list</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="nv">@list</span>
<span class="k">end</span>
</code></pre></div></div>

<p>And functions to send them:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Message</span> <span class="k">do</span>
  <span class="nv">@moduledoc</span> <span class="sd">"""
  List of all messages
  """</span>

  <span class="nv">@list</span> <span class="sx">~w[
      app_one_hello
      app_two_hello
    ]a</span>

  <span class="k">def</span> <span class="n">list</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="nv">@list</span>

  <span class="no">Enum</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="nv">@list</span><span class="p">,</span> <span class="k">fn</span> <span class="n">message</span> <span class="o">-&gt;</span>
    <span class="k">def</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">message</span><span class="p">)(</span><span class="n">payload</span> <span class="p">\\</span> <span class="no">nil</span><span class="p">)</span> <span class="k">do</span>
      <span class="no">GenStage</span><span class="o">.</span><span class="n">cast</span><span class="p">(</span>
        <span class="no">Dispatcher</span><span class="p">,</span>
        <span class="p">{</span><span class="ss">:message</span><span class="p">,</span>
         <span class="p">%{</span>
           <span class="ss">name:</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">message</span><span class="p">),</span>
           <span class="ss">payload:</span> <span class="n">payload</span>
         <span class="p">}}</span>
      <span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Great! We can now send a message with:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Dispatcher</span><span class="o">.</span><span class="no">Message</span><span class="o">.</span><span class="n">app_one_hello</span><span class="p">(</span><span class="s2">"some_payload"</span><span class="p">)</span>
</code></pre></div></div>

<p>There are few advantages of this:</p>

<ul>
  <li>compiler will tell us about typos</li>
  <li>IDE will autocomplete function name</li>
</ul>

<p>Now we gonna take care of listener.</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Listener</span> <span class="k">do</span>
  <span class="nv">@moduledoc</span> <span class="sd">"""
  Listener for messages from dispatcher
  """</span>
  <span class="nv">@callback</span> <span class="n">on_message</span><span class="p">(</span><span class="n">message</span> <span class="p">::</span> <span class="n">map</span><span class="p">)</span> <span class="p">::</span> <span class="n">any</span>

  <span class="nv">@doc</span> <span class="no">false</span>
  <span class="k">defmacro</span> <span class="n">__using__</span><span class="p">(</span><span class="n">_opts</span><span class="p">)</span> <span class="k">do</span>
    <span class="kn">quote</span> <span class="ss">location:</span> <span class="ss">:keep</span> <span class="k">do</span>
      <span class="nv">@behaviour</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Listener</span>

      <span class="kn">use</span> <span class="no">GenStage</span>
      <span class="kn">import</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Listener</span>

      <span class="k">def</span> <span class="n">start_link</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span> <span class="k">do</span>
        <span class="no">GenStage</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="bp">__MODULE__</span><span class="p">,</span> <span class="n">opts</span><span class="p">,</span> <span class="ss">name:</span> <span class="bp">__MODULE__</span><span class="p">)</span>
      <span class="k">end</span>

      <span class="k">def</span> <span class="n">init</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="k">do</span>
        <span class="p">{</span><span class="ss">:consumer</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="ss">subscribe_to:</span> <span class="p">[</span><span class="no">Dispatcher</span><span class="p">]}</span>
      <span class="k">end</span>

      <span class="k">def</span> <span class="n">handle_events</span><span class="p">(</span><span class="n">messages</span><span class="p">,</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
        <span class="n">for</span> <span class="n">message</span> <span class="o">&lt;-</span> <span class="n">messages</span> <span class="k">do</span>
          <span class="n">on_message</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
        <span class="k">end</span>

        <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="p">[],</span> <span class="n">state</span><span class="p">}</span>
      <span class="k">end</span>

      <span class="k">def</span> <span class="n">on_message</span><span class="p">(</span><span class="n">_</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:nothing</span>
      <span class="n">defoverridable</span> <span class="ss">on_message:</span> <span class="mi">1</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="no">Enum</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="no">Dispatcher</span><span class="o">.</span><span class="no">Message</span><span class="o">.</span><span class="n">list</span><span class="p">(),</span> <span class="k">fn</span> <span class="n">message</span> <span class="o">-&gt;</span>
    <span class="k">defmacro</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">message</span><span class="p">)(</span><span class="n">payload</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span> <span class="k">do</span>
      <span class="n">message</span> <span class="o">=</span> <span class="no">Macro</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="kn">unquote</span><span class="p">(</span><span class="n">message</span><span class="p">))</span>
      <span class="n">payload</span> <span class="o">=</span> <span class="no">Macro</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
      <span class="n">block</span> <span class="o">=</span> <span class="no">Macro</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">block</span><span class="p">,</span> <span class="kn">unquote</span><span class="p">:</span> <span class="no">true</span><span class="p">)</span>

      <span class="kn">quote</span> <span class="ss">bind_quoted:</span> <span class="p">[</span><span class="ss">message:</span> <span class="n">message</span><span class="p">,</span> <span class="ss">payload:</span> <span class="n">payload</span><span class="p">,</span> <span class="ss">block:</span> <span class="n">block</span><span class="p">]</span> <span class="k">do</span>
        <span class="k">def</span> <span class="n">on_message</span><span class="p">(%{</span><span class="ss">name:</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="ss">payload:</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">payload</span><span class="p">)}),</span> <span class="k">do</span><span class="p">:</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">block</span><span class="p">)</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span><span class="p">)</span>

  <span class="k">defmacro</span> <span class="n">other</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">name</span> <span class="o">=</span> <span class="no">Macro</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
    <span class="n">payload</span> <span class="o">=</span> <span class="no">Macro</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
    <span class="n">block</span> <span class="o">=</span> <span class="no">Macro</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">block</span><span class="p">,</span> <span class="kn">unquote</span><span class="p">:</span> <span class="no">true</span><span class="p">)</span>

    <span class="kn">quote</span> <span class="ss">bind_quoted:</span> <span class="p">[</span><span class="ss">name:</span> <span class="n">name</span><span class="p">,</span> <span class="ss">payload:</span> <span class="n">payload</span><span class="p">,</span> <span class="ss">block:</span> <span class="n">block</span><span class="p">]</span> <span class="k">do</span>
      <span class="k">def</span> <span class="n">on_message</span><span class="p">(%{</span><span class="ss">name:</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">name</span><span class="p">),</span> <span class="ss">payload:</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">payload</span><span class="p">)}),</span> <span class="k">do</span><span class="p">:</span> <span class="kn">unquote</span><span class="p">(</span><span class="n">block</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Looks a little bit complicated, but it’s not!</p>

<p>We’ve just built a simple DSL for receiving messages. Now the listening module looks like this:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defmodule</span> <span class="no">AppOne</span><span class="o">.</span><span class="no">Listener</span> <span class="k">do</span>
  <span class="nv">@moduledoc</span> <span class="sd">"""
  AppOne listener
  """</span>
  <span class="kn">use</span> <span class="no">Dispatcher</span><span class="o">.</span><span class="no">Listener</span>

  <span class="n">app_one_hello</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="k">do</span>
    <span class="no">AppOne</span><span class="o">.</span><span class="n">hello</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="n">other</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">do</span>
    <span class="ss">:nothing</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Again! Typos are handled by the compiler, and we can use IDE to complete functions names.</p>

<h3 id="summary">Summary</h3>

<p>I’ve used message name for all of the functions. If you want you can use prefix those function. For example <code class="language-plaintext highlighter-rouge">send_app_one_hello</code> and <code class="language-plaintext highlighter-rouge">on_app_one_hello</code>.</p>

<p>Check out my github for example app:</p>

<ul>
  <li>
    <p><a href="https://github.com/fazibear/avoiding_typos/tree/master">first version</a></p>
  </li>
  <li>
    <p><a href="https://github.com/fazibear/avoiding_typos/tree/with_meta">with metaprogramming</a></p>
  </li>
</ul>]]></content><author><name>Michał Kalbarczyk</name></author><category term="elixir" /><category term="compiler" /><category term="contracts" /><category term="typos" /><summary type="html"><![CDATA[In this article, I want to describe how you can use elixir metaprogramming to avoid some runtime errors caused by typos. If you’re passing around some handcrafted messages ex. %{name: "app_one_hello", payload: "payload"} you can easily introduce errors. Sending app_one_hello and listening for app_ane_hello will not make your system works correctly. Imagine you have thousands of messages. It’s really hard to manage them like that. To make things better, you can create a list of all messages and generate functions to send and receive them. Now when you will create a typo, elixir’s compiler will tell you!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.fazibear.me/images/stories/typo.jpg" /><media:content medium="image" url="https://blog.fazibear.me/images/stories/typo.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>