<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: protium</title>
    <description>The latest articles on DEV Community by protium (@protium).</description>
    <link>https://dev.to/protium</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F57513%2Fa942e705-8b47-4a00-ac4f-302983772dd5.jpeg</url>
      <title>DEV Community: protium</title>
      <link>https://dev.to/protium</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/protium"/>
    <language>en</language>
    <item>
      <title>An interpreted language you can try in my terminal website</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Thu, 17 Nov 2022 18:20:30 +0000</pubDate>
      <link>https://dev.to/protium/an-interpreted-language-you-can-try-in-my-terminal-website-i1</link>
      <guid>https://dev.to/protium/an-interpreted-language-you-can-try-in-my-terminal-website-i1</guid>
      <description>&lt;p&gt;A few weeks ago I started reading &lt;a href="https://interpreterbook.com/"&gt;Writing An Interpreter In Go&lt;/a&gt; and &lt;br&gt;
while reading it and implementing the interpreter a question popped up: what if I can run this&lt;br&gt;
interpreter in the terminal in my website?&lt;/p&gt;

&lt;p&gt;To recap, a few months ago I decided to transform my personal &lt;a href="https://protiumx.dev"&gt;website&lt;/a&gt; into &lt;br&gt;
a terminal (read more about it &lt;a href="https://protiumx.dev/blog/posts/my-profile-website-is-now-a-terminal/"&gt;here&lt;/a&gt;).&lt;br&gt;
In today's post I'll give an overview of the whole implementation, but first a preview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ApnqTeX2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://protiumx.dev/blog/posts/an-interpreted-language-you-can-try-in-my-terminal-website/preview-simia.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ApnqTeX2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://protiumx.dev/blog/posts/an-interpreted-language-you-can-try-in-my-terminal-website/preview-simia.gif" alt="preview" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try it by typing &lt;code&gt;simia&lt;/code&gt; in the terminal &lt;a href="https://protiumx.dev"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The book: a great resource for learning about interpreters
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I8MVcjN7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/protiumx/blog/blob/main/content/posts/repl-in-terminal-website/book-cover.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I8MVcjN7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/protiumx/blog/blob/main/content/posts/repl-in-terminal-website/book-cover.png%3Fraw%3Dtrue" alt="book cover" width="293" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although this post is not a book review, I'd like to say that it was a really nice reading.&lt;br&gt;
You can learn from the basics of tokenization to the evaluation of the parsed input.&lt;br&gt;
The author does a great job guiding the reader through all the concepts with concrete examples.&lt;br&gt;
If you want to learn about how interpreters and REPL work this book is definitely a great start!&lt;/p&gt;
&lt;h2&gt;
  
  
  Extending the Monkey language
&lt;/h2&gt;

&lt;p&gt;In the book, the author introduces us to the &lt;code&gt;Monkey&lt;/code&gt; language.&lt;br&gt;
Its syntax is quite simple and that motivated me to add more expressions and syntax, essentially extending&lt;br&gt;
the language to my desire. I wanted to create a language that would combine syntax from &lt;code&gt;go&lt;/code&gt;, &lt;code&gt;rust&lt;/code&gt;, and&lt;br&gt;
&lt;code&gt;elixir&lt;/code&gt;. And that's how &lt;a href="https://github.com/protiumx/simia"&gt;simia&lt;/a&gt; was born.&lt;br&gt;
I'm still working on it but so far I have added support for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Range&lt;/code&gt; expressions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;for-loop&lt;/code&gt; expressions along with the infix operator &lt;code&gt;in&lt;/code&gt;. e.g.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for a in 1..10 { 
  log(a); 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Support for boolean condition in &lt;code&gt;for-loop&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for a &amp;gt; 0 { 
  log(a); 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Parentheses are optional for &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;for-loop&lt;/code&gt; expressions&lt;/li&gt;
&lt;li&gt;Support for elixir's &lt;code&gt;pipe&lt;/code&gt; operator &lt;code&gt;|&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8 |&amp;gt; factorial() |&amp;gt; add(100)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Re-assign variables with &lt;code&gt;=&lt;/code&gt; operator
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let foo = "bar";
foo = "1234";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And in my TODO list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support modules&lt;/li&gt;
&lt;li&gt;Support &lt;code&gt;pub&lt;/code&gt; key&lt;/li&gt;
&lt;li&gt;Add more built-ins for arrays, strings, and maps&lt;/li&gt;
&lt;li&gt;Support &lt;code&gt;mut&lt;/code&gt; key and control mutability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, I want &lt;code&gt;simia&lt;/code&gt; to be some sort of modern functional programming language.&lt;/p&gt;
&lt;h2&gt;
  
  
  Loading the interpreter in the browser: WebAssembly
&lt;/h2&gt;

&lt;p&gt;If you are not familiar with &lt;code&gt;wasm&lt;/code&gt;, here a quote from &lt;a href="https://webassembly.org/"&gt;https://webassembly.org/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.&lt;br&gt;
Wasm is designed as a portable compilation target for programming languages, enabling deployment on&lt;br&gt;
the web for client and server applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically it allows us to convert the &lt;code&gt;simia&lt;/code&gt; interpreter (written with golang) into a set of instructions that the browser&lt;br&gt;
can understand and execute.&lt;/p&gt;

&lt;p&gt;Doing this in &lt;code&gt;golang&lt;/code&gt; was really simple thanks to the &lt;a href="https://pkg.go.dev/syscall/js"&gt;syscall/js&lt;/a&gt;&lt;br&gt;
package and the arch &lt;code&gt;wasm&lt;/code&gt; when compiling the interpreter.&lt;br&gt;
Let's see a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// main.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"syscall/js"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wasmHash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FuncOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"hello "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;channel&lt;/code&gt; we keep waiting for input. This is necessary because the wasm module &lt;strong&gt;it is an application&lt;/strong&gt;&lt;br&gt;
and not a library. So this application should keep running and not exit as soon as &lt;code&gt;js.Global()...&lt;/code&gt; is executed.&lt;/p&gt;

&lt;p&gt;Now we compile the app to wasm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GOOS=js GOARCH=wasm go build -o hello.wasm ./main.go 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to load our wasm we need to use the wasm tools go provides&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" . 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now the final bit, we load the wasm from js and use our wonderful &lt;code&gt;hello&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"wasm_exec.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instantiateStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello.wasm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importObject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;universe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// hello universe&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it, serving the file and opening it in a browser should show the result in the developer console.&lt;br&gt;
I took exactly the same steps to compile and use the &lt;code&gt;simia&lt;/code&gt; interpreter in the browser.&lt;br&gt;
The wasm module exports to objects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;simia&lt;/code&gt;: a function that evaluates simia code and returns the result&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;simia_version&lt;/code&gt;: the version of the interpreter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;p&gt;All was going beautifully but I had to step back to think about a problem: &lt;code&gt;simia&lt;/code&gt; has a built-in function&lt;br&gt;
called &lt;code&gt;log&lt;/code&gt; which prints values to the stdout using the &lt;code&gt;fmt&lt;/code&gt; package.&lt;br&gt;
When compiled to wasm, the &lt;code&gt;fmt.Println&lt;/code&gt; will use &lt;code&gt;console.log&lt;/code&gt; as a buffer, so any &lt;code&gt;log&lt;/code&gt; instruction will be print&lt;br&gt;
into the browser console.&lt;/p&gt;

&lt;p&gt;The solution is to modify the built-in to use a buffer and then pass some sort of buffer from javascript,&lt;br&gt;
so then I can use that buffer to print to the terminal element. I will probably will solve this during the&lt;br&gt;
weekend, but if you have a better idea please let me know in the comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending my website terminal
&lt;/h2&gt;

&lt;p&gt;When I came up with the idea of adding the REPL into my website, I took a look at the existing code in my website&lt;br&gt;
and identified the requirements needed to make this happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The wasm module can be loaded on demand, i.e. the first time the user calls &lt;code&gt;simia&lt;/code&gt; from the terminal&lt;/li&gt;
&lt;li&gt;Commands should be able &lt;strong&gt;take&lt;/strong&gt; the std in and out.&lt;/li&gt;
&lt;li&gt;The REPL will use the &lt;code&gt;wasm&lt;/code&gt; module to eval the user input&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ctrl-c&lt;/code&gt; should "kill" the process and return to the shell&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other to implement this requirements I have prepared a few refactors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improve error handling, e.g. "command missing", "wrong arguments", etc&lt;/li&gt;
&lt;li&gt;Improve key presses handling and extract common functionality&lt;/li&gt;
&lt;li&gt;Introduce &lt;code&gt;processId&lt;/code&gt;. When a command execution returns a process Id it means that the main function, let's call it
shell, should stop processing the input. Once the command exits it should call a callback to restore the main function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it, I can say that now my terminal has a fully working simia REPL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;I had a lot of fun writing the interpreter and loading it into my terminal website, I definitely learned &lt;br&gt;
a lot of things in the process. I think writing an interpreter is quite a good practice for any developer.&lt;/p&gt;

&lt;p&gt;All the changes I made to my website were introduced in this &lt;a href="https://github.com/protiumx/protiumx.github.io/pull/4/files"&gt;PR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What else would you add to the language or the terminal?&lt;br&gt;
I'm planning to do some sort of treasure hunt in my website, stay tuned!&lt;/p&gt;

&lt;p&gt;👽&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>go</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Github template for Golang services</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Sun, 19 Jun 2022 20:26:51 +0000</pubDate>
      <link>https://dev.to/protium/github-template-for-golang-services-3o27</link>
      <guid>https://dev.to/protium/github-template-for-golang-services-3o27</guid>
      <description>&lt;p&gt;As a weekend project I created a github template that can be very handy for creating go services with relational databases.&lt;br&gt;
Let's take a look at what is included.&lt;/p&gt;
&lt;h2&gt;
  
  
  Task Runner
&lt;/h2&gt;

&lt;p&gt;For many years, GNU &lt;code&gt;make&lt;/code&gt; has been my to-go tool to run &lt;code&gt;rules&lt;/code&gt; and &lt;code&gt;tasks&lt;/code&gt; for any sort of project. It is&lt;br&gt;
fairly simple to use but it can also become complex as some rules might require to execute external tools or&lt;br&gt;
even declare bash functions with in a rule definition. I must admit that the developer experience can&lt;br&gt;
be rough for those who haven't used &lt;code&gt;make&lt;/code&gt; before.&lt;/p&gt;

&lt;p&gt;Developer experience is a very important topic to me and I decided to use this project to find a reliable&lt;br&gt;
alternative to &lt;code&gt;make&lt;/code&gt;. And that's how I found &lt;a href="https://taskfile.dev/"&gt;Task&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Task is a task runner / build tool that aims to be simpler and easier to use than, for example, GNU Make.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After checking some examples and its &lt;a href="https://taskfile.dev/api/"&gt;API&lt;/a&gt; docs, I got convinced I should give it a try.&lt;br&gt;
Although I'm not a fan of &lt;code&gt;yaml&lt;/code&gt; I found some neat features that I prefer over &lt;code&gt;make&lt;/code&gt;:&lt;/p&gt;
&lt;h3&gt;
  
  
  Import env variables
&lt;/h3&gt;

&lt;p&gt;Makefile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="k"&gt;include&lt;/span&gt;&lt;span class="sx"&gt; .env&lt;/span&gt;
&lt;span class="err"&gt;$(eval&lt;/span&gt; &lt;span class="k"&gt;export &lt;/span&gt;&lt;span class="err"&gt;$(shell&lt;/span&gt; &lt;span class="err"&gt;sed&lt;/span&gt; &lt;span class="err"&gt;-ne&lt;/span&gt; &lt;span class="s1"&gt;'s/ *#.*$$//; /./ s/=.*$$// p'&lt;/span&gt; &lt;span class="err"&gt;.env))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Taskfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dotenv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.env'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Showing help
&lt;/h3&gt;

&lt;p&gt;Although &lt;code&gt;make&lt;/code&gt; doesn't create a &lt;code&gt;help&lt;/code&gt; command, there is a very common pattern to define one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;help&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="c"&gt;##&lt;/span&gt;&lt;span class="nf"&gt; print this help&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'&lt;/span&gt; &lt;span class="nv"&gt;$(MAKEFILE_LIST)&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Task&lt;/code&gt; provides a list of available commands when running &lt;code&gt;task -l&lt;/code&gt;, but you need to add a &lt;code&gt;desc&lt;/code&gt; field to each command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run:
  desc: Run go app
  cmds:
    - go run cmd/main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CLI args
&lt;/h3&gt;

&lt;p&gt;When working with &lt;code&gt;make&lt;/code&gt; you can pass arguments/variables a target, for instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="err"&gt;$(name)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To pass the argument you would call it like &lt;code&gt;make hello name=dev&lt;/code&gt;. This can become tedious when passing multiple args to the inner command.&lt;br&gt;
A simple hack to allow cli args is to filter out those &lt;code&gt;args&lt;/code&gt; from the make &lt;code&gt;goals&lt;/code&gt; and also avoid errors when&lt;br&gt;
targets are not found:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Filter out make goals from CLI args
&lt;/span&gt;&lt;span class="nv"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;filter-out&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;,&lt;span class="nv"&gt;$(MAKECMDGOALS)&lt;/span&gt;&lt;span class="nf"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Do nothing if target not found
&lt;/span&gt;&lt;span class="nl"&gt;%&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;:
&lt;span class="nl"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="err"&gt;$(args)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works fine unless the arguments have a value that matches the name of any target.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;code&gt;task&lt;/code&gt; provides a simple template variable with the arguments &lt;code&gt;CLI_ARGS&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cmds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo {{.CLI_ARGS}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command can then be called as &lt;code&gt;task hello -- world&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Verbose file targets definition
&lt;/h3&gt;

&lt;p&gt;Although &lt;code&gt;make&lt;/code&gt; file targets are not difficult to understand, I think &lt;code&gt;task&lt;/code&gt; syntax is easier to understand&lt;br&gt;
for a new dev. Let's compare them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;pkg/db/%.go&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;db/queries/%.sql&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;${CURDIR}&lt;/span&gt;:/src &lt;span class="nt"&gt;-w&lt;/span&gt; /src kjconroy/sqlc generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Taskfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;db-gen&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;desc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate queries code using Sqlc&lt;/span&gt;
  &lt;span class="na"&gt;cmds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run --rm -v $pwd:/src -w /src kjconroy/sqlc generate&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db/queries/*.sql&lt;/span&gt;
  &lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pkg/db/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far &lt;code&gt;task&lt;/code&gt; seems to be a very good alternative to &lt;code&gt;make&lt;/code&gt;, at least for my personal use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Folder structure
&lt;/h2&gt;

&lt;p&gt;The folder structure of this template is based on folder structures I've seen across many go repos,&lt;br&gt;
which I really like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- `cmd/`: app entry points
- `db/`:
  - `migrations/`: SQL migrations files
  - `queries/`: SQL query files used by `sqlc`
- `pkg/`: app sources
  - `db/`: code generated by `sqlc`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database
&lt;/h2&gt;

&lt;p&gt;As mentioned before, this template is for a go service with a relational database.&lt;br&gt;
As a personal preference I chose &lt;code&gt;postgres&lt;/code&gt; as db engine.&lt;/p&gt;
&lt;h3&gt;
  
  
  Schema
&lt;/h3&gt;

&lt;p&gt;I have recently adopted the practice of defining my db schemas using &lt;a href="https://www.dbml.org/"&gt;dbml&lt;/a&gt;&lt;br&gt;
and generating the sql code using their CLI tool. Example:&lt;br&gt;
This schema&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table users {
  id uuid [pk]
  user_name text [not null, unique]
  password_hash bytea [not null]
  created_at timestamptz [not null, default: `now() at time zone 'utc'`]
  updated_at timestamptz [not null, default: `now() at time zone 'utc'`]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will generate this SQL code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;"id"&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;"user_name"&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;"password_hash"&lt;/span&gt; &lt;span class="n"&gt;bytea&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;"created_at"&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;at&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="s1"&gt;'utc'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nv"&gt;"updated_at"&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;at&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="s1"&gt;'utc'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Queries and migrations
&lt;/h3&gt;

&lt;p&gt;After having worked with different ORMs (goent, gorm) and plain &lt;code&gt;go sql&lt;/code&gt; code, I find that having a &lt;br&gt;
middle ground is always the most versatile option. This middle ground is about having control over raw SQL&lt;br&gt;
queries and the ability to generate code to run all those queries and represent models in code.&lt;/p&gt;

&lt;p&gt;For this matter I chose &lt;a href="https://github.com/kyleconroy/sqlc"&gt;sqlc&lt;/a&gt; which offers a good amount of features like&lt;br&gt;
support for different dbs and db drivers. Regarding db migrations, I prefer to run them isolated from the code. There are countless tools to manage&lt;br&gt;
migrations but recently I've been sticking with &lt;a href="https://github.com/golang-migrate/migrate"&gt;go-migrate&lt;/a&gt;&lt;br&gt;
which also provides a go library in case I want to integrate the migrations into the code.&lt;/p&gt;

&lt;p&gt;To run both tools, I've created &lt;code&gt;tasks&lt;/code&gt; that will use their docker images.&lt;br&gt;
Alternatively, there could be a task to install them into the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;The template provides a multi-stage &lt;code&gt;Dockerfile&lt;/code&gt;. It uses the official &lt;code&gt;golang:1.18&lt;/code&gt; image for building and&lt;br&gt;
a &lt;code&gt;scrath&lt;/code&gt; image to copy the binaries.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;docker-compose&lt;/code&gt; file can be used to get a db instance and run migrations on it. In this cases the migrations&lt;br&gt;
service waits for the postgres service to be ready, so we only need to run &lt;code&gt;docker-compose up -d&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI
&lt;/h2&gt;

&lt;p&gt;A github actions workflow is provided to run &lt;code&gt;go fmt, vet, test&lt;/code&gt; and &lt;a href="https://github.com/securego/gosec"&gt;gosec&lt;/a&gt;.&lt;br&gt;
An initial configuration for &lt;code&gt;dependabot&lt;/code&gt; is also provided.&lt;/p&gt;

&lt;p&gt;That's it, go take a look at the repo &lt;a href="https://github.com/protiumx/template-go-service"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading 👽&lt;/p&gt;

&lt;p&gt;Other posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://protiumx.dev/blog/posts/creating-a-text-based-ui-with-rust/"&gt;Creating a Text-based UI with rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protiumx.dev/blog/posts/my-profile-website-is-now-a-terminal/"&gt;My profile website is now a terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protiumx.dev/blog/posts/publish-your-blog-articles-everywhere-with-this-github-action/"&gt;Publish your blog articles everywhere with this github action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>github</category>
      <category>database</category>
      <category>docker</category>
    </item>
    <item>
      <title>Creating a Text-based UI with rust</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Thu, 02 Jun 2022 20:48:08 +0000</pubDate>
      <link>https://dev.to/protium/creating-a-text-based-ui-with-rust-420</link>
      <guid>https://dev.to/protium/creating-a-text-based-ui-with-rust-420</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Continuing with my last project &lt;a href="https://github.com/protiumx/rq" rel="noopener noreferrer"&gt;rq&lt;/a&gt;, I recently started to work on&lt;br&gt;
this project card: &lt;a href="https://github.com/protiumx/rq/projects/1#card-82259239" rel="noopener noreferrer"&gt;Implement interactive prompt&lt;/a&gt;.&lt;br&gt;
Let's have a look how can we implement a &lt;strong&gt;text-based UI&lt;/strong&gt; with &lt;code&gt;rust&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The ANSI Standard
&lt;/h2&gt;

&lt;p&gt;Before we dive into the wonderful world of terminal emulators and text-based interfaces, &lt;br&gt;
we need to understand what the &lt;strong&gt;ANSI escape sequences&lt;/strong&gt; are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ANSI escape sequences are a standard for in-band signaling to control cursor location, color, font styling, and other options on video text terminals and terminal emulators. &lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://en.wikipedia.org/wiki/ANSI_escape_code" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/ANSI_escape_code&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By using ANSI sequences we can tell the terminal emulator how to render text, change the cursor position, &lt;br&gt;
clear the screen, etc. In my last &lt;a href="https://dev.to/protium/my-profile-website-is-now-a-terminal-2j57"&gt;post&lt;/a&gt;&lt;br&gt;
I used these sequences to implement a very basic shell for my &lt;a href="https://protiumx.dev" rel="noopener noreferrer"&gt;profile website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's see some examples. Head to your favorite terminal emulator (I use &lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer"&gt;kitty&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1b[35;47mANSI? &lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1b[0m&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1b[1;32mSI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fansi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fansi.png" alt="echo ANSI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;\x1b&lt;/code&gt;: this is the hexadecimal for &lt;code&gt;ESC&lt;/code&gt; (escape). It starts the escape sequence&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[&lt;/code&gt;: is the &lt;a href="https://en.wikipedia.org/wiki/ANSI_escape_code#CSIsection" rel="noopener noreferrer"&gt;control sequence introducer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;35;47m&lt;/code&gt;: the SGR (Select Graphic Rendition) where &lt;code&gt;35&lt;/code&gt; sets the &lt;code&gt;foreground&lt;/code&gt; color to magenta and
&lt;code&gt;47&lt;/code&gt; sets the &lt;code&gt;background&lt;/code&gt; to white. &lt;code&gt;m&lt;/code&gt; is the final character specified by the standard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\x1b[0m&lt;/code&gt;: it's a special sequence to reset or turn all attributes off&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\x1b[1;32m&lt;/code&gt;: Sets foreground to green and add the &lt;code&gt;bold&lt;/code&gt; attribute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets try this in rust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[35;47mANSI? &lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[1;32mSI"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should have the same result as with the &lt;code&gt;echo&lt;/code&gt; command above.&lt;/p&gt;

&lt;p&gt;Another example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[2J"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the standard &lt;code&gt;[2J&lt;/code&gt; erases entire display.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the text-based interface
&lt;/h2&gt;

&lt;p&gt;As a loyal user of (neo)vim, I love the concept of buffers. I want my app to have 2 buffers,&lt;br&gt;
one for the list of HTTP requests and one for the current response:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fdesign.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fdesign.jpg" alt="design preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start from scratch. To enter into the "interactive" mode, we need to clear the whole screen.&lt;br&gt;
For that we will use the following escape sequences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\x1b&lt;/span&gt;&lt;span class="s"&gt;[2J&lt;/span&gt;&lt;span class="se"&gt;\r\x1b&lt;/span&gt;&lt;span class="s"&gt;[H"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://i.giphy.com/media/l3q2K5jinAlChoCLS/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l3q2K5jinAlChoCLS/giphy.gif" alt="wut"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;\r&lt;/code&gt;, carriage return. We go all the way to the left&lt;/li&gt;
&lt;li&gt;As explained before, &lt;code&gt;[2J&lt;/code&gt; clears the entire screen&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[H&lt;/code&gt; moves the cursor to &lt;code&gt;home&lt;/code&gt;, or the corner top-left of the terminal (col 1, row 1).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nice, an empty screen.&lt;br&gt;
Next I want to draw the requests the program loaded  from the &lt;code&gt;http&lt;/code&gt; file &lt;br&gt;
(for context: I have published this &lt;a href="https://protiumx.dev/blog/posts/an-http-request-parser-with-rust-and-pest.rs/" rel="noopener noreferrer"&gt;article&lt;/a&gt;&lt;br&gt;
about how to parse HTTP request with rust). Based on the design, the selected request should print the &lt;br&gt;
HTTP method in green, URL in white and body in orange. If the request is selected, the whole request line should be green.&lt;br&gt;
Let's write a helper function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;draw_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;request_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{} {} HTTP/{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;colorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.method&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.version&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.header&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;request_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;{:?}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.body&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;request_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;colorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Orange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.body&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you followed along my last post, you may have an idea of what the &lt;code&gt;colorize&lt;/code&gt; function looks like.&lt;/p&gt;

&lt;p&gt;Now we are able to print all the request. Since we will use &lt;code&gt;&amp;gt;&lt;/code&gt; to visually mark which request is &lt;br&gt;
selected, we need to cater some space for it, hence we will print two empty spaces before each request start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;http_file&lt;/span&gt;&lt;span class="py"&gt;.requests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  {}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// go home &lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[H"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// print the cursor &amp;gt;&lt;/span&gt;
&lt;span class="nd"&gt;print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[32m&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Results:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Frequest.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Frequest.png" alt="print request"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  User interaction
&lt;/h2&gt;

&lt;p&gt;The user should be able to navigate between requests with the &lt;strong&gt;arrow keys&lt;/strong&gt;. To achieve this we will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track the current cursor position (column and row)&lt;/li&gt;
&lt;li&gt;Move the cursor down or up according to the user input&lt;/li&gt;
&lt;li&gt;Redraw the interface (clean the screen and draw)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can compare this flow to how a game engine works: we evaluate changes and use the engine to draw the game &lt;br&gt;
in its current state.&lt;/p&gt;

&lt;p&gt;Since I want to finish this project in less than a month, I have decided to take a look at the different&lt;br&gt;
options in &lt;code&gt;rust&lt;/code&gt; for building a &lt;code&gt;TUI&lt;/code&gt;. There are a few options that I can use as a TUI "engine".&lt;/p&gt;

&lt;p&gt;I opted for &lt;a href="https://github.com/fdehau/tui-rs/" rel="noopener noreferrer"&gt;tui-rs&lt;/a&gt; which is being used by some of my favorite &lt;br&gt;
terminal apps like &lt;a href="https://github.com/ClementTsang/bottom" rel="noopener noreferrer"&gt;bottom&lt;/a&gt;. I headed over the &lt;a href="https://github.com/fdehau/tui-rs/tree/master/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt;&lt;br&gt;
and used the &lt;code&gt;list&lt;/code&gt; example as a base.&lt;/p&gt;

&lt;p&gt;After a small refactor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Frame&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Split the screen into 2 vertical portions&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;.direction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Horizontal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;.constraints&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nn"&gt;Constraint&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Percentage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nn"&gt;Constraint&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Percentage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nf"&gt;.size&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ListItem&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
      &lt;span class="py"&gt;.requests&lt;/span&gt;
      &lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;ListItem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;draw_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;list_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.borders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Borders&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Requests"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;.highlight_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nn"&gt;Style&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
              &lt;span class="nf"&gt;.add_modifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BOLD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="nf"&gt;.fg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;.highlight_symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt; "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nf"&gt;.render_stateful_widget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="nf"&gt;.block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_block&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we are at the same point as when using ANSI escape sequences to render the UI.&lt;br&gt;
This time we can easily implement a key handler and deal with the user interaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="py"&gt;.code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Down&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Up&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.previous&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;app&lt;/code&gt; is an instance of the &lt;code&gt;App&lt;/code&gt; struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ListState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// from tui-rs&lt;/span&gt;
    &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_buffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.list&lt;/span&gt;&lt;span class="nf"&gt;.selected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.requests&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.list&lt;/span&gt;&lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.list&lt;/span&gt;&lt;span class="nf"&gt;.selected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.requests&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.list&lt;/span&gt;&lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet! Now it is possible to navigate between requests:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fnavigation.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fnavigation.gif" alt="navigation"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Sending HTTP requests
&lt;/h2&gt;

&lt;p&gt;Once the app is interactive and we can select a request, we can implement a handler for the &lt;code&gt;Enter&lt;/code&gt; key as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// impl App ...&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;selected_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.requests&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.list&lt;/span&gt;&lt;span class="nf"&gt;.selected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//....&lt;/span&gt;

&lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.seleted_request&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;rq_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;request&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.response_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have implemented the &lt;code&gt;execute&lt;/code&gt; function in the last post, you can take a look at the source &lt;a href="https://github.com/protiumx/rq/blob/main/rq-core/src/request.rs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The UI is practically finished: the user can navigate with arrow keys and hit &lt;code&gt;Enter&lt;/code&gt; to execute a request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Multi-threading
&lt;/h2&gt;

&lt;p&gt;The problem we are facing now is that, even though the request is executed &lt;code&gt;async&lt;/code&gt; with &lt;code&gt;tokio&lt;/code&gt;, it is &lt;br&gt;
blocking the main thread because we are &lt;code&gt;awaiting&lt;/code&gt; it. If we send a slow request, the UI won't receive updates nor &lt;br&gt;
key events until the request is done.&lt;/p&gt;

&lt;p&gt;A proper solution would be to &lt;code&gt;spawn&lt;/code&gt; a new thread to perform the request and assign the response to the app &lt;code&gt;response_buffer&lt;/code&gt; once it finishes.&lt;br&gt;
If you have worked before with a compiled language like &lt;code&gt;C++&lt;/code&gt; or &lt;code&gt;C#&lt;/code&gt;, you will know that we need to access to &lt;br&gt;
the &lt;code&gt;app&lt;/code&gt; variable in a &lt;strong&gt;thread-safe&lt;/strong&gt; way by using a &lt;code&gt;mutex&lt;/code&gt; (mutual exclusion object):&lt;br&gt;
In short, we want to &lt;strong&gt;block&lt;/strong&gt; any other thread from accessing to the &lt;code&gt;app&lt;/code&gt; state at the same time that we want to change it.&lt;/p&gt;

&lt;p&gt;Furthermore, we also need to take into account the lifetime checks made by the rust compiler.&lt;br&gt;
If we simply &lt;strong&gt;move&lt;/strong&gt; a variable into a new thread, its ownership is moved and thus the variable is killed once &lt;br&gt;
the thread finishes. The solution? use a &lt;code&gt;pointer&lt;/code&gt; to "leak" the &lt;code&gt;app&lt;/code&gt; variable into another thread.&lt;/p&gt;

&lt;p&gt;Without entering into details that are out of the scope of this article, I will use an &lt;a href="https://doc.rust-lang.org/std/sync/struct.Arc.html" rel="noopener noreferrer"&gt;Atomically Reference Counted&lt;/a&gt;,&lt;br&gt;
from the rust &lt;strong&gt;standard library&lt;/strong&gt;, to create a shared pointer of a &lt;code&gt;Mutex&lt;/code&gt;. The multi-thread implementation looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app_arc: Arc&amp;lt;tokio::sync::Mutex&amp;lt;App&amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create a new reference&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;app_arc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_arc&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// the ownership of app_arc was now moved into this thread&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;rq_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;request&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="c1"&gt;// here we use the tokio Mutex to lock our variable since we want to write data into it&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_arc&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.response_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// at this point the thread exists and app_arc is killed.&lt;/span&gt;
      &lt;span class="c1"&gt;// app is also killed, unlocking it.&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fresult.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprotiumx.dev%2Fblog%2Fposts%2Fcreating-a-text-based-ui-with-rust%2Fresult.gif" alt="result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we can navigate while a request is being executed. The UI keeps responsive. Perfect!&lt;/p&gt;

&lt;p&gt;This is it for today, I hope you learned something new.&lt;/p&gt;

&lt;p&gt;You can check out the repo &lt;a href="https://github.com/protiumx/rq" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
If you would like to collaborate with this project there are some TODOs over &lt;a href="https://github.com/protiumx/rq/projects/1" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other articles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://protiumx.dev/blog/posts/my-profile-website-is-now-a-terminal/" rel="noopener noreferrer"&gt;My profile website is now a terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protiumx.dev/blog/posts/an-http-request-parser-with-rust-and-pest.rs/" rel="noopener noreferrer"&gt;An HTTP request parser with rust and pest.rs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;My dotfiles: &lt;a href="https://github.com/protiumx/.dotfiles" rel="noopener noreferrer"&gt;https://github.com/protiumx/.dotfiles&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;My blog source: &lt;a href="https://github.com/protiumx/blog" rel="noopener noreferrer"&gt;https://github.com/protiumx/blog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👽&lt;/p&gt;

</description>
      <category>rust</category>
      <category>terminal</category>
      <category>tui</category>
      <category>http</category>
    </item>
    <item>
      <title>My profile website is now a terminal</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Wed, 25 May 2022 12:51:03 +0000</pubDate>
      <link>https://dev.to/protium/my-profile-website-is-now-a-terminal-2j57</link>
      <guid>https://dev.to/protium/my-profile-website-is-now-a-terminal-2j57</guid>
      <description>&lt;p&gt;When I was younger I used to think that my profile website would be a really cool, fully featured website, with shiny colors and animations; built with the latest cutting edge frontend technology...&lt;br&gt;
Turns out that the older I get, the more I prefer a simple terminal. No UI, just text and commands.&lt;/p&gt;

&lt;p&gt;The last time I updated my profile website, it looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Fcontent%2Fposts%2F006%2Flast.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Fcontent%2Fposts%2F006%2Flast.png" alt="last profle website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was already pretty minimalistic, right? But not enough. Now my profile website is just a terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Fcontent%2Fposts%2F006%2Fterm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Fcontent%2Fposts%2F006%2Fterm.png" alt="terminal profile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see how this was possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pragmatic approach
&lt;/h2&gt;

&lt;p&gt;A few days ago I was shaping this idea on my head and found this cool library: &lt;a href="https://xtermjs.org/" rel="noopener noreferrer"&gt;xterms&lt;/a&gt;. It's been used by a lot of apps, VS Code being among them. I decided to give it a try to see how complex could it be, so I headed to the &lt;a href="https://xtermjs.org/docs/" rel="noopener noreferrer"&gt;docs&lt;/a&gt; and started adding the code to my website. As you can see the docs are pretty good, they are surely autogenerated from TS docs but this is good because it means the code itself is well documented.&lt;/p&gt;

&lt;p&gt;Before starting coding I set a few requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't want to use &lt;code&gt;npm&lt;/code&gt; modules. I want my website source to be simple and minimal&lt;/li&gt;
&lt;li&gt;I want to make use of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;javascript modules&lt;/a&gt; which are supported by all (relevant) browsers&lt;/li&gt;
&lt;li&gt;The terminal commands should be abstract to allow me to remove or add commands at will with a few changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, how do I install &lt;code&gt;xtermjs&lt;/code&gt; without using &lt;code&gt;npm&lt;/code&gt;? The solution is simple, I host the files. I extracted the files from the npm packages with this command&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm v xterm dist.tarball | xargs curl | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and moved &lt;code&gt;package/lib/xterm.js&lt;/code&gt; into &lt;code&gt;app/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To use javascript modules, I just needed to import the &lt;code&gt;main.js&lt;/code&gt; file as module&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/app/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Terminal Commands
&lt;/h2&gt;

&lt;p&gt;Although not using &lt;code&gt;typescript&lt;/code&gt; let's say that the terminal commands implement the following interface&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Command&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Terminal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then we need a command runner that will parse the user input&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CommandRunner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Terminal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The runner will return &lt;code&gt;false&lt;/code&gt; if a command was not found. &lt;br&gt;
Let's now define 1 command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lsCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ls&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[usage]: ls filename&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\t\t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that we shaped the &lt;code&gt;command&lt;/code&gt;, we can think of handling user input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminal basic functionality
&lt;/h2&gt;

&lt;p&gt;The terminal should support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It should show a &lt;code&gt;prompt&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ctrl + l&lt;/code&gt;: should clear the terminal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ctrl + c&lt;/code&gt;: should send a &lt;code&gt;SIGINT&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;enter&lt;/code&gt;: should run a command from the current user input&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The terminal should also handle common errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;command not found&lt;/li&gt;
&lt;li&gt;command with wrong arguments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this in mind we can start handling the user input.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;xterm&lt;/code&gt; provides a &lt;code&gt;onKey&lt;/code&gt; event which receives a handler function &lt;code&gt;({ key, domEvent }) =&amp;gt; void&lt;/code&gt;, so we receive an event per each key press done by the user. This means that we need to track the user input and add each key as a char. When the user presses &lt;code&gt;enter&lt;/code&gt; we should evaluate the input we have so far. Pretty straigt forward&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; &lt;code&gt;xterm&lt;/code&gt; doesn't render the user input, so we need to do it when it makes sense (not enter, not an arrow key, etc)&lt;/p&gt;

&lt;p&gt;Handling the clear-screen can be implemented as&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctrlKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;l&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and the &lt;code&gt;SIGINT&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctrlKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;At this point we have a pretty basic working terminal, so let's add some more commands&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic commands
&lt;/h2&gt;

&lt;p&gt;What are the most known commands? For my terminal I want to be able to use &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;exit&lt;/code&gt;. But remember that this terminal is actually my profile website, so they should make sense in that context. So I decided the terminal should have a file system, where files are shaped like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;about.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;once upon a time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}];&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With this in mind, &lt;code&gt;cat&lt;/code&gt; will print the file content, &lt;code&gt;ls&lt;/code&gt; will print each file's name and &lt;code&gt;rm&lt;/code&gt; will delete the file from the array.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;exit&lt;/code&gt; command we can just close the window from javascript: &lt;code&gt;window.close()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3knKct3fGqxhK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3knKct3fGqxhK/giphy.gif" alt="hacker man"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;p&gt;I have decided that I wanted to have a file named &lt;code&gt;blog.md&lt;/code&gt; which should contain my last 5 posts.&lt;br&gt;
To fetch this info, I used the RSS feed xml file generated by &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;hugo&lt;/a&gt; for my &lt;a href="https://protiumx.dev/blog/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. All I need to do is to fetch the file, parse the &lt;code&gt;xml&lt;/code&gt; document and get the title and links of each post:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fecthLastPosts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blog/index.xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DOMParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xmlDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/xml&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;xmlDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;nodeValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;nodeValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;lastPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;`\r\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\r\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now &lt;code&gt;cat blog.md&lt;/code&gt; prints my last 5 posts, and thanks to the &lt;code&gt;web link&lt;/code&gt; addon of &lt;code&gt;xterm&lt;/code&gt; each link is clickeable. Noice.&lt;br&gt;
But why stopping here? Every &lt;code&gt;hackerman&lt;/code&gt; terminal should have a &lt;code&gt;whoami&lt;/code&gt; command. So this command will just print information about my self.&lt;/p&gt;

&lt;p&gt;Also, cool web apps contain photos of cats, so I decided to write a &lt;code&gt;randc&lt;/code&gt; command what will open a random photo of a cat.&lt;br&gt;
For this I found this amazing &lt;a href="https://cataas.com/#/" rel="noopener noreferrer"&gt;rest API&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;randc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get a random cat photo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getting a cato...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cataas.com/cat?json=true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[error] no catos today :( -- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;colorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TermColors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opening cato...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cataas.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Fcontent%2Fposts%2F006%2Frandc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Fcontent%2Fposts%2F006%2Frandc.gif" alt="get a cat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think this should do it for a &lt;code&gt;profile terminal&lt;/code&gt;. I'm very satisfied with the simplicity of it and the commands I have implemented.&lt;br&gt;
I'll problaly add more commands in the future and also implement &lt;code&gt;streams&lt;/code&gt; , just for fun.&lt;/p&gt;

&lt;p&gt;What command would you add to your &lt;strong&gt;profile terminal&lt;/strong&gt;?&lt;br&gt;
Go have some fun with it: &lt;a href="https://protiumx.dev" rel="noopener noreferrer"&gt;https://protiumx.dev&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Update:
&lt;/h2&gt;

&lt;p&gt;I have refactored the project structure to improve readability and make it more generic.&lt;br&gt;
It also loads your command history from the local storage. All the changes can be seen here: &lt;a href="https://github.com/protiumx/protiumx.github.io/pull/1" rel="noopener noreferrer"&gt;https://github.com/protiumx/protiumx.github.io/pull/1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Update 2:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rm&lt;/code&gt; supports glob pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Update 3:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;added &lt;code&gt;man&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;added &lt;code&gt;uname&lt;/code&gt; command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://protiumx.dev/blog/posts/publish-your-blog-articles-everywhere-with-this-github-action/" rel="noopener noreferrer"&gt;Publish your blog articles everywhere with this github action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protiumx.dev/blog/posts/an-http-request-parser-with-rust-and-pest.rs/" rel="noopener noreferrer"&gt;An HTTP request parser with rust and pest.rs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👽&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>javascript</category>
      <category>terminal</category>
      <category>xtermjs</category>
    </item>
    <item>
      <title>An HTTP request parser with rust and pest.rs</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Mon, 23 May 2022 21:43:05 +0000</pubDate>
      <link>https://dev.to/protium/an-http-request-parser-with-rust-and-pestrs-2p</link>
      <guid>https://dev.to/protium/an-http-request-parser-with-rust-and-pestrs-2p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I happened to have learned about &lt;a href="https://en.wikipedia.org/wiki/Parsing_expression_grammar" rel="noopener noreferrer"&gt;parsing expression grammars&lt;/a&gt; a few days ago and got really excited about writing my own grammar.&lt;br&gt;
As I was missing this &lt;a href="https://github.com/Huachao/vscode-restclient" rel="noopener noreferrer"&gt;VS Code extension&lt;/a&gt; when working on &lt;code&gt;neovim&lt;/code&gt;, an idea popped up:&lt;br&gt;
&lt;strong&gt;What if I write an HTTP grammar and an execute request from an interactive prompt?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Preview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fpreview.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fpreview.gif" alt="preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;

&lt;p&gt;For our grammar, we need to make sure we understand the HTTP standard defined &lt;a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Request = Request-Line ; Section 5.1
        *(( general-header        ; Section 4.5
         | request-header         ; Section 5.3
         | entity-header ) CRLF)  ; Section 7.1
        CRLF
        [ message-body ]          ; Section 4.3


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's breakout the definition&lt;/p&gt;

&lt;h3&gt;
  
  
  Request
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;Request&lt;/code&gt; is conformed by a &lt;code&gt;Request-Line&lt;/code&gt; optionally followed by &lt;code&gt;headers&lt;/code&gt; and a new line (a &lt;strong&gt;Carriage Return&lt;/strong&gt; and &lt;strong&gt;Line Feed&lt;/strong&gt;) then another new line. After the &lt;code&gt;headers&lt;/code&gt; we find the &lt;code&gt;message-body&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request-Line
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Request-Line = Method SP Request-URI SP HTTP-Version CRLF


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we should fine a &lt;code&gt;Method&lt;/code&gt; followed by a white space. Then the &lt;code&gt;Request-URI&lt;/code&gt; followed by a white space. Finally the &lt;code&gt;HTTP-Version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An example of a request can be &lt;code&gt;GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.\r\n&lt;/code&gt; (note the end of the line)&lt;/p&gt;

&lt;h3&gt;
  
  
  Request-Header
&lt;/h3&gt;

&lt;p&gt;Defined as&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

equest-header = Accept                   ; Section 14.1
      | Accept-Charset           ; Section 14.2
      | Accept-Encoding          ; Section 14.3
      | Accept-Language          ; Section 14.4
      | Authorization            ; Section 14.8
      | Expect                   ; Section 14.20
      | From                     ; Section 14.22
      | Host                     ; Section 14.23
      | If-Match                 ; Section 14.24
      | If-Modified-Since        ; Section 14.25
      | If-None-Match            ; Section 14.26
      | If-Range                 ; Section 14.27
      | If-Unmodified-Since      ; Section 14.28
      | Max-Forwards             ; Section 14.31
      | Proxy-Authorization      ; Section 14.34
      | Range                    ; Section 14.35
      | Referer                  ; Section 14.36
      | TE                       ; Section 14.39
      | User-Agent               ; Section 14.43


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;let's open one of those sections to check the format.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Authorization = "Authorization" ":" credentials


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Basically this header is the word &lt;code&gt;Authorization&lt;/code&gt; followed by &lt;code&gt;:&lt;/code&gt; and then the &lt;code&gt;credentials&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Message-Body
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

message-body = entity-body
                    | &amp;lt;entity-body encoded as per Transfer-Encoding&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For simplicity let's assume that the &lt;code&gt;body&lt;/code&gt; can be &lt;strong&gt;anything&lt;/strong&gt; that is not a request line nor a header.&lt;/p&gt;

&lt;p&gt;Now we have enough information to write our http grammar, more specific a set of &lt;strong&gt;rules&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pest
&lt;/h2&gt;

&lt;p&gt;To write our grammar we will use &lt;a href="https://pest.rs/" rel="noopener noreferrer"&gt;pest&lt;/a&gt;. From the website:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;pest is a general purpose parser written in Rust with a focus on &lt;strong&gt;accessibility&lt;/strong&gt;, &lt;strong&gt;correctness&lt;/strong&gt;, and &lt;strong&gt;performance&lt;/strong&gt;. It uses &lt;a href="https://en.wikipedia.org/wiki/Parsing_expression_grammar" rel="noopener noreferrer"&gt;parsing expression grammars (or PEG)&lt;/a&gt; as input, which are similar in spirit to regular expressions, but which offer the enhanced expressivity needed to parse complex languages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds great!&lt;/p&gt;

&lt;p&gt;Pest has its &lt;a href="https://pest.rs/book/grammars/syntax.html" rel="noopener noreferrer"&gt;own syntax&lt;/a&gt; for writing grammar rules. A &lt;strong&gt;rule&lt;/strong&gt; is defined as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

my_rule = { ... }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's create a new rule &lt;code&gt;my_rule= { "test" }&lt;/code&gt;. This rule will match the word &lt;code&gt;test&lt;/code&gt; inside an &lt;strong&gt;input&lt;/strong&gt;. So if we want to parse a file that only contains "test" or new lines  we need to define the shape of a line with the delimiters &lt;code&gt;SOI&lt;/code&gt; and &lt;code&gt;EOI&lt;/code&gt; (start and end of input respectively)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

testy_file = {
    SOI
    ~ (my_rule | NEWLINE)*
    ~ EOI
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's test this out in the online grammar editor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fpest-editor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fpest-editor.png" alt="editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The parser has identified a &lt;code&gt;testy_file&lt;/code&gt; that contains to matches for the rule &lt;code&gt;my_rule&lt;/code&gt;. So far so good.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP Grammar
&lt;/h2&gt;

&lt;p&gt;Now we are ready for writing our grammar (are we?). We need to write this grammar as close as possible to the standard, since it's defined in a similar way, with &lt;strong&gt;rules&lt;/strong&gt;. We can imagine something like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

request = { 
    request_line ~
    headers? ~
    NEWLINE ~
    body?
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our &lt;code&gt;request&lt;/code&gt; rule says: a &lt;code&gt;request_line&lt;/code&gt; optionally followed by &lt;code&gt;headers&lt;/code&gt; followed by a new line, then optionally followed by the &lt;code&gt;body&lt;/code&gt;. With optionally I make reference to the &lt;code&gt;?&lt;/code&gt; mark which is a repetition operator (those who are familiar with regex expressions should understand this right away) and means that something &lt;code&gt;can occur zero or one times&lt;/code&gt;. &lt;br&gt;
For our &lt;code&gt;request_line&lt;/code&gt; we will match the standard as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

request_line = _{ method ~ " "+ ~ uri ~ " "+ ~ "HTTP/" ~ version ~ NEWLINE }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We are talking about a &lt;code&gt;method&lt;/code&gt; followed by a blank space then the &lt;code&gt;uri&lt;/code&gt; then another blank space then the &lt;code&gt;http version&lt;/code&gt; followed by a &lt;code&gt;NEWLINE&lt;/code&gt;. &lt;code&gt;NEWLINE&lt;/code&gt; is a pest built-in rule that matches &lt;code&gt;"\n" | "\r\n" | "\r"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's break the request line.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;method&lt;/code&gt; we just need to match any of the http methods defined in the standard&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Method = "OPTIONS"                ; Section 9.2
        | "GET"                    ; Section 9.3
        | "HEAD"                   ; Section 9.4
        | "POST"                   ; Section 9.5
        | "PUT"                    ; Section 9.6
        | "DELETE"                 ; Section 9.7
        | "TRACE"                  ; Section 9.8
        | "CONNECT"                ; Section 9.9


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Therefore our method rule should be&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

method = { ("GET" | "DELETE" | "POST" | "PUT" | ...) }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here our rule says: match the exact word &lt;code&gt;GET&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt; or ... you get the idea.&lt;/p&gt;

&lt;p&gt;Next, for simplicity purpuses, we will say that the &lt;code&gt;uri&lt;/code&gt; is anything that is &lt;strong&gt;not&lt;/strong&gt; a blank space&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

uri = { (!whitespace ~ ANY)+ }
whitespace = _{ " " | "\t" }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; here we also defined that the &lt;code&gt;white space&lt;/code&gt; can be a space or a tab. Note that &lt;code&gt;_&lt;/code&gt; means that the rule is &lt;a href="https://pest.rs/book/grammars/syntax.html#silent-and-atomic-rules" rel="noopener noreferrer"&gt;silent&lt;/a&gt;; it does not produce tokens.&lt;/p&gt;

&lt;p&gt;The HTTP version goes like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

version = { (ASCII_DIGIT | ".")+ }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Meaning: any digit or a dot character. In our &lt;code&gt;request_line&lt;/code&gt; the version is defined as the word "HTTP/" followed by the &lt;code&gt;version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we require a &lt;code&gt;NEWLINE&lt;/code&gt; at the end of our request line.&lt;/p&gt;

&lt;p&gt;Let's test this out on the editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Frequest-line.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Frequest-line.png" alt="request line"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! Note that the &lt;code&gt;whitespace&lt;/code&gt; rule does not produce a token, we only see &lt;code&gt;method&lt;/code&gt;, &lt;code&gt;uri&lt;/code&gt; and &lt;code&gt;version&lt;/code&gt;. Let's continue with the headers.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

header = { header_name ~ ":" ~ whitespace ~ header_value ~ NEWLINE }
header_name = { (!(":" | NEWLINE) ~ ANY)+ }
header_value = { (!NEWLINE ~ ANY)+ }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In here we say that a &lt;code&gt;header_name&lt;/code&gt; is anything except for &lt;code&gt;:&lt;/code&gt; , because this character is a &lt;strong&gt;header separator&lt;/strong&gt;, it delimits the header name. We also don't want a new line, we will see why bellow.&lt;br&gt;
The name is followed by the separator &lt;code&gt;:&lt;/code&gt; and then 1 white space.&lt;br&gt;
The &lt;code&gt;header_value&lt;/code&gt; is anything except for a &lt;strong&gt;new line&lt;/strong&gt;, because the new line delimits a &lt;code&gt;header&lt;/code&gt;. After the value we encounter a new line.&lt;/p&gt;

&lt;p&gt;Let's test this again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fheaders.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fheaders.png" alt="headers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can se that the parser matches 1 header as &lt;code&gt;{ header_name: "auth", header_value: "token" }&lt;/code&gt;. Did you notice that it also say &lt;code&gt;headers &amp;gt; header&lt;/code&gt; ? That is because we also want a rule that can matches a 1 or more &lt;code&gt;header&lt;/code&gt; rules. We define this rule as:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

headers = { header+ }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This way the pest parser will produce a token &lt;code&gt;headers&lt;/code&gt; with a list of &lt;code&gt;header&lt;/code&gt; matches.&lt;/p&gt;

&lt;p&gt;Lastly the body:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

body = { ANY+ }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The body is anything after the headers&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fbody.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fbody.png" alt="body"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far our grammar can parse 1 request from the input. Coming back to the original idea for this post, we want to be able to parse multiple http requests from a file. But here we have a conflict because our &lt;code&gt;body&lt;/code&gt; rule will match anything after the headers, and that anything can be another request.&lt;br&gt;
To solve this problem we need the help of a ✨ delimiter ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  The .http file syntax
&lt;/h2&gt;

&lt;p&gt;To delimit each http request in our file we will make use of 3 &lt;code&gt;#&lt;/code&gt; symbols (as the VS Code rest client does).&lt;br&gt;
Let's go add this to our grammar:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

delimiter = { "#"{3} ~ NEWLINE+ }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our delimiter is exactly 3 number signs followed by 1 or more new lines. Then we can say that the &lt;code&gt;body&lt;/code&gt; is anything except for a delimiter:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

body = { (!delimiter ~ ANY)+ }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's test this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fmultiple.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fmultiple.png" alt="multiple requests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice! So finally we can define what our http file should look like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

file = { SOI ~ (delimiter | request)* ~ EOI}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our http file is composed by delimiters or requests, 0 or more of them.&lt;br&gt;
We have completed our http grammar, now it's time to get &lt;strong&gt;rusty&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fschwifty.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fschwifty.jpeg" alt="get schwifty"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing http files
&lt;/h2&gt;

&lt;p&gt;We will refer to &lt;a href="https://pest.rs/book/examples/ini.html" rel="noopener noreferrer"&gt;this example&lt;/a&gt; for setting up the base code &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;pest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;#[macro_use]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;pest_derive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pest&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Parser)]&lt;/span&gt;
&lt;span class="nd"&gt;#[grammar&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"grammar.pest"&lt;/span&gt;&lt;span class="nd"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HttpParser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We will use the following file for testing&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

GET https://protiumx.github.io HTTP/1.1
authorization: token

###

POST https://rq-rust.free.beeceptor.com/api HTTP/1.1

{
  "hello": "hola"
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I set up a mock api in &lt;a href="https://beeceptor.com/" rel="noopener noreferrer"&gt;https://beeceptor.com/&lt;/a&gt;. It might not be available by the time you are reading this. But you can use any endpoint that accepts &lt;strong&gt;POST&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's go ahead an parse the file&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;unparsed_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test.http"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cannot read file"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HttpParser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;unparsed_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unsuccessful parse"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// unwrap the parse result&lt;/span&gt;
        &lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// get and unwrap the `file` rule; never fails&lt;/span&gt;

        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:#?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;we get: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Pair {
    rule: file,
    span: Span {
        str: "GET https://protiumx.github.io HTTP/1.1\nauthorzation: token\n\n###\n\nPOST https://rq-rust.free.beeceptor.com/api HTTP/1.1\n\n{\n  \"hello\": \"hola\"\n}\n",
        start: 0,
        end: 142,
    },
    inner: [
        Pair {
            rule: request,
            span: Span {
                str: "GET https://protiumx.github.io HTTP/1.1\nauthorzation: token\n\n",
                start: 0,
                end: 61,
            },
            inner: [
                Pair {
                    rule: method,
                    span: Span {
                        str: "GET",
                        start: 0,
                        end: 3,
                    },
                    inner: [],
                },
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Pair&lt;/code&gt; struct has a pretty clear structure and we could use to print each inner &lt;code&gt;Pair&lt;/code&gt;. But we want to get rusty so we will put some effort.&lt;/p&gt;

&lt;p&gt;We will define an &lt;code&gt;HttpFile&lt;/code&gt; struct. This struct will contain a vector of &lt;code&gt;HttpRequest&lt;/code&gt;.&lt;br&gt;
Let's define them:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;HttpMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Put&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HttpFile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And to get even more rusty we will implement the &lt;code&gt;TryFrom&lt;/code&gt; trait to each struct. Let's implement it for the &lt;code&gt;HttpFile&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'i&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;TryFrom&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;HttpFile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="nf"&gt;.into_inner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="nf"&gt;.as_rule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EOI&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nn"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;Pair&amp;lt;'i, Rule&amp;gt;&lt;/code&gt; has the lifetime of the &lt;code&gt;input&lt;/code&gt;.&lt;br&gt;
As we know, the &lt;code&gt;file&lt;/code&gt; rule can contain 0 or more of (delimiter | request). This means that in its content we can find: delimiter, request or End of File.&lt;br&gt;
Here we will &lt;strong&gt;try&lt;/strong&gt; to parse each request, so we need the &lt;code&gt;TryFrom&lt;/code&gt; trait for the &lt;code&gt;HttpRequest&lt;/code&gt; struct&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'i&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;TryFrom&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="nf"&gt;.into_inner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="nf"&gt;.as_rule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="nf"&gt;.parse_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="nf"&gt;.into_inner&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nn"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="py"&gt;.body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;unreachable!&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fegghead.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fegghead.jpeg" alt="egghead"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each &lt;code&gt;request&lt;/code&gt; can have 5 inner matches: method, url, version, headers and body.&lt;br&gt;
The first 3 do not have inner rules, so we can just extract them as &lt;code&gt;&amp;amp;str&lt;/code&gt;. After this, the iterator can only have &lt;code&gt;headers&lt;/code&gt; or &lt;code&gt;body&lt;/code&gt; pairs. For the &lt;code&gt;headers&lt;/code&gt; we perform a similar operation:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pairs&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="nf"&gt;.into_inner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Grapes! We are pretty much done with the parser. Now let's use the parsed content to make the requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  reqwest
&lt;/h2&gt;

&lt;p&gt;After a quick check of the available rust http client libraries I opted for &lt;a href="https://crates.io/crates/reqwest" rel="noopener noreferrer"&gt;reqwest&lt;/a&gt;. It has a pretty simple API and it seems to be among the most used libraries for this matters. But I'm a bit concerned about all its dependencies so I might try &lt;a href="https://github.com/algesten/ureq" rel="noopener noreferrer"&gt;ureq&lt;/a&gt; later.&lt;/p&gt;

&lt;p&gt;For this part I just followed the examples on the docs website and ended up with this code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="nd"&gt;#[derive(Default)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;reqwest&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;blocking&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;HeaderMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;HeaderValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ACCEPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;HeaderValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;reqwest&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;blocking&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nf"&gt;.default_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.no_gzip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;
            &lt;span class="py"&gt;.client&lt;/span&gt;
            &lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.method&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HeaderMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="py"&gt;.body&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="nf"&gt;.text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What we can see in here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;code&gt;ClientBuilder&lt;/code&gt; to add some default headers and config for the request&lt;/li&gt;
&lt;li&gt;Parse our &lt;code&gt;HttpMethod&lt;/code&gt; into &lt;code&gt;reqwest::Method&lt;/code&gt;. &lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;HttpMethod&lt;/code&gt; implements the &lt;code&gt;Display&lt;/code&gt; trait.&lt;/li&gt;
&lt;li&gt;Parse our &lt;code&gt;HashMap&amp;lt;String, String&lt;/code&gt; of headers into &lt;code&gt;reqwest::header::HeaderMap&lt;/code&gt;. This is possible because it implements the &lt;code&gt;TryFrom&amp;lt;HashMap&amp;lt;String, String&amp;gt;&amp;gt;&lt;/code&gt; trait.&lt;/li&gt;
&lt;li&gt;Finally clone the body and send the request. We output the body of the request as &lt;code&gt;text&lt;/code&gt; (this consumes the body)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We are not trimming the body, perhaps we should.&lt;/p&gt;

&lt;p&gt;No we are ready for the last part: the interactive prompt&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazyness kicks in
&lt;/h2&gt;

&lt;p&gt;At this point I felt a bit lazy to implement the prompt so I searched for existing solutions, one of them being &lt;a href="https://github.com/mikaelmello/inquire" rel="noopener noreferrer"&gt;inquire&lt;/a&gt;. It has a pretty straight forward API, so to show an interactive select we just need this lines:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;http_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;file_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Select requests to execute:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_file&lt;/span&gt;&lt;span class="py"&gt;.requests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.prompt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that the &lt;code&gt;HttpRequest&lt;/code&gt; implements the &lt;code&gt;Display&lt;/code&gt; trait. &lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fprompt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F005%2Fprompt.png" alt="prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gorgeous.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unintroduction
&lt;/h2&gt;

&lt;p&gt;This project turned out to be quite fun! I ended up naming it &lt;code&gt;rq&lt;/code&gt; and you can see its source &lt;a href="https://github.com/protiumx/rq/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My goal is to have a full interactive prompt that allows you to execute many request without existing the prompt mode.&lt;br&gt;
If you would like to collaborate I have created a few TODO cards &lt;a href="https://github.com/protiumx/rq/projects/1" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's it. Thanks for reading 👽&lt;/p&gt;

&lt;p&gt;Others posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/protium/publish-your-blog-articles-everywhere-with-this-github-action-3g6k"&gt;Publish your blog articles everywhere with this github action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/protium/coding-problems-tdd-and-ci-282n"&gt;Coding Problems, TDD, and CI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/protium/your-new-pretty-and-minimalist-resume-with-latex-421j"&gt;Your new pretty and minimalist resume with LaTex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>peg</category>
      <category>http</category>
      <category>rest</category>
    </item>
    <item>
      <title>Automate an articles section in your github.io page</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Fri, 13 May 2022 17:26:41 +0000</pubDate>
      <link>https://dev.to/protium/automate-an-articles-section-in-your-githubio-page-4ooe</link>
      <guid>https://dev.to/protium/automate-an-articles-section-in-your-githubio-page-4ooe</guid>
      <description>&lt;p&gt;Today I wanted to update my &lt;a href="https://protiumx.github.io/"&gt;github page&lt;/a&gt; to show a list of my&lt;br&gt;
last Medium &lt;a href="https://medium.com/@protiumx"&gt;articles&lt;/a&gt;. I ended up automating it.&lt;/p&gt;
&lt;h2&gt;
  
  
  First approach
&lt;/h2&gt;

&lt;p&gt;My first attempt was fetching the RSS feed using the URL &lt;code&gt;medium.com/feed/@username&lt;/code&gt; and parse&lt;br&gt;
the &lt;code&gt;xml&lt;/code&gt; document. But I was hit with a charming &lt;code&gt;CORS&lt;/code&gt; error.&lt;br&gt;
Now what?&lt;/p&gt;

&lt;p&gt;I remembered that I'm using a github action to update a similar section on my &lt;a href="https://github.com/protiumx"&gt;github profile&lt;/a&gt;.&lt;br&gt;
So, what if I tell this action that the README file is called &lt;code&gt;index.html&lt;/code&gt;?&lt;/p&gt;
&lt;h2&gt;
  
  
  blog-post-workflow
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/gautamkrishnar/blog-post-workflow"&gt;github action&lt;/a&gt; supports a &lt;code&gt;readme_path&lt;/code&gt; parameter. After &lt;br&gt;
a quick dive in its source code I noticed that this file could be anything, not necessarily a markdown file. Problem solved!&lt;/p&gt;

&lt;p&gt;Let's add the articles section into the html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content__articles"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;gt;&lt;/span&gt; last published articles:&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- BLOG-POST-LIST:START --&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- BLOG-POST-LIST:END --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the action is intended for markdown files, the default template for the posts links is &lt;code&gt;[title](url)&lt;/code&gt; but we need html code.&lt;br&gt;
Luckily the github action also provides a &lt;code&gt;template&lt;/code&gt; param. So let's change the template to generate list items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"$url"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;$title&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;$newline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;Our workflow configuration should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update Medium articles&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0'&lt;/span&gt; &lt;span class="c1"&gt;# Runs once a week&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update-articles-section&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gautamkrishnar/blog-post-workflow@master&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;feed_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://medium.com/feed/@protiumx"&lt;/span&gt;
          &lt;span class="na"&gt;readme_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/protiumx/blog/main/articles/004/index.html"&lt;/span&gt;
          &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;href="$url"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;target="_blank"&amp;gt;$title&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;$newline'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the file &lt;a href="https://github.com/protiumx/protiumx.github.io/blob/main/.github/workflows/medium-articles.yml"&gt;here&lt;/a&gt;.&lt;br&gt;
NOTE: I added the &lt;code&gt;workflow_dispatch&lt;/code&gt; trigger so I can trigger the action from the github UI.&lt;/p&gt;

&lt;p&gt;That's all. I few lines of code and we added an automated section for our github page (or any page).&lt;/p&gt;

&lt;p&gt;Related articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/protium/publish-your-blog-articles-everywhere-with-this-github-action-3g6k"&gt;Publish your blog articles everywhere with this github action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👽&lt;/p&gt;

</description>
      <category>ci</category>
      <category>githubaction</category>
      <category>githubpages</category>
      <category>automation</category>
    </item>
    <item>
      <title>kitty + zsh + powerlevel10k = ✨ aesthetics ✨</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Mon, 09 May 2022 19:49:40 +0000</pubDate>
      <link>https://dev.to/protium/kitty-zsh-powerlevel10k-aesthetics-1e81</link>
      <guid>https://dev.to/protium/kitty-zsh-powerlevel10k-aesthetics-1e81</guid>
      <description>&lt;p&gt;Who doesn't like a good looking terminal and useful layouts and keymaps?&lt;br&gt;
This has always been my goal since I started using linux as main OS.&lt;/p&gt;

&lt;p&gt;I have used lots of different terminals, plugins and settings over the years but I think now &lt;br&gt;
I achieved the glory with my current setup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F003%2Fsetup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fprotiumx%2Fblog%2Fmain%2Farticles%2F003%2Fsetup.png" alt="setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's have a look to the configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do I use?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Terminal: &lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer"&gt;Kitty&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Shell: &lt;code&gt;zsh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;zsh config management: &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;Oh my Zsh!&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;zsh theme: &lt;a href="https://github.com/romkatv/powerlevel10k" rel="noopener noreferrer"&gt;powerlevel10k&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;editor: &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;neovim&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Terminal
&lt;/h3&gt;

&lt;p&gt;To achieve a clean look in the terminal, I changed the opacity of kitty, removed the window title bar,&lt;br&gt;
and changed margins. Here the configuration needed:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

background_opacity 0.76
draw_minimal_borders yes
window_padding_width 2
window_border_width 0
hide_window_decorations yes
titlebar-only yes
active_border_color none


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I have also added changes for the tabs bar in order to make it look minimalist:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

tab_bar_edge top
tab_bar_background none
tab_bar_style powerline
tab_powerline_style slanted
tab_title_template "{fmt.fg.c2c2c2}{title}"

active_tab_title_template "{fmt.fg._fff}{title}"
active_tab_foreground #fff
active_tab_font_style bold-italic
active_tab_background #8631B4

inactive_tab_foreground #c2c2c2
inactive_tab_background #8631B4


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In order to move quick between splits I added the following mappings:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

map cmd+shift+up neighboring_window up
map cmd+shift+left neighboring_window left
map cmd+shift+right neighboring_window right
map cmd+shift+down neighboring_window down


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  powerlevel10k
&lt;/h3&gt;

&lt;p&gt;There is not much to mention here since I have used the powerlevel10k script to configure it.&lt;br&gt;
Once you install powerlevel10k it should init the configuration wizard. If you want to re-configure&lt;br&gt;
do so by running &lt;code&gt;p10k configure&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;p&gt;You can check my powerlevel10k config file &lt;a href="https://github.com/protiumx/.dotfiles/blob/main/stow/zsh/.p10k.zsh" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  neovim
&lt;/h3&gt;

&lt;p&gt;Plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/vim-airline/vim-airline" rel="noopener noreferrer"&gt;vim-airline&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this case, I got rid of many highlights background colors&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;

&lt;span class="nb"&gt;highlight&lt;/span&gt; LineNr ctermbg&lt;span class="p"&gt;=&lt;/span&gt;none
&lt;span class="nb"&gt;highlight&lt;/span&gt; Normal ctermbg&lt;span class="p"&gt;=&lt;/span&gt;none
&lt;span class="nb"&gt;highlight&lt;/span&gt; NonText ctermbg&lt;span class="p"&gt;=&lt;/span&gt;none
&lt;span class="nb"&gt;highlight&lt;/span&gt; SignColumn ctermbg&lt;span class="p"&gt;=&lt;/span&gt;none
&lt;span class="nb"&gt;highlight&lt;/span&gt; VertSplit ctermbg&lt;span class="p"&gt;=&lt;/span&gt;none ctermfg&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;98&lt;/span&gt; cterm&lt;span class="p"&gt;=&lt;/span&gt;none


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I have also setup a color column to show a visual limit at 100 chars&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

set colorcolumn=100
highlight ColorColumn ctermbg=93


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;My vim-airline config goes like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

let g:airline_theme='selenized_bw'

" Show git branch
let g:airline#extensions#branch#enabled=1

let g:airline#extensions#hunks#enabled=0
let g:airline_powerline_fonts=1
let g:airline_detect_spell=0

" Short version for modes
let g:airline_mode_map = {
      \ '__'     : '-',
      \ 'c'      : 'C',
      \ 'i'      : 'I',
      \ 'ic'     : 'I',
      \ 'ix'     : 'I',
      \ 'n'      : 'N',
      \ 'multi'  : 'M',
      \ 'ni'     : 'N',
      \ 'no'     : 'N',
      \ 'R'      : 'R',
      \ 'Rv'     : 'R',
      \ 's'      : 'S',
      \ 'S'      : 'S',
      \ ''     : 'S',
      \ 't'      : 'T',
      \ 'v'      : 'V',
      \ 'V'      : 'V',
      \ ''     : 'V',
      \ }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's it! The setup is fairly simple and the results are quite good.&lt;/p&gt;

&lt;p&gt;You can have a look at all my config and dotfiles in &lt;a href="https://github.com/protiumx/.dotfiles/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PS: this article has its source on &lt;a href="https://github.com/protiumx/blog/blob/main/articles/003/content.md" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;br&gt;
I'm using a github action to publish it to different platforms. Read more &lt;br&gt;
&lt;a href="https://dev.to/protium/publish-your-blog-articles-everywhere-with-this-github-action-3g6k"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/protium/bash-gnu-stow-take-a-walk-while-your-new-macbook-is-being-set-up-p1o"&gt;Bash + GNU Stow: take a walk while your new macbook is being set up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/protium/kitty-configuration-for-the-iterm-user-pe4"&gt;Kitty configuration for the iTerm user&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/protium/vim-delightful-settings-and-plugins-18am"&gt;Vim: Delightful settings and plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👽&lt;/p&gt;

</description>
    </item>
    <item>
      <title>blogpub@v0.4.1: host media on github for your blog posts</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Sun, 20 Feb 2022 17:56:37 +0000</pubDate>
      <link>https://dev.to/protium/blogpubv041-host-media-on-github-for-your-blog-posts-435a</link>
      <guid>https://dev.to/protium/blogpubv041-host-media-on-github-for-your-blog-posts-435a</guid>
      <description>&lt;h1&gt;
  
  
  &lt;a href="mailto:blogpub@v0.4.1"&gt;blogpub@v0.4.1&lt;/a&gt;: host media on github for your blog posts
&lt;/h1&gt;

&lt;p&gt;Hey everyone, this is just a small update on the &lt;a href="https://github.com/marketplace/actions/blogpub"&gt;blogpub&lt;/a&gt; action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relative paths
&lt;/h2&gt;

&lt;p&gt;In release &lt;a href="https://github.com/protiumx/blogpub/releases/tag/v0.4.1"&gt;v0.4.1&lt;/a&gt; I added support for relative &lt;br&gt;
paths which means you can now use &lt;strong&gt;github&lt;/strong&gt; to host the media you use in your blog posts.&lt;br&gt;
How does it work? Just use relative paths (to the markdown file) and you are ready to go.&lt;br&gt;
E.g.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3tClnCta--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/protiumx/blog/main/articles/blog-002/magic.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3tClnCta--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/protiumx/blog/main/articles/blog-002/magic.gif" alt="magic" width="237" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The action replaces all relative paths with the &lt;strong&gt;raw content&lt;/strong&gt; github urls.&lt;br&gt;
In the example &lt;code&gt;.magic.gif&lt;/code&gt; is replaced with &lt;code&gt;https://raw.githubusercontent.com/protiumx/blog/articles/blog-002/magic.gif&lt;/code&gt;.&lt;br&gt;
Check out the code &lt;a href="https://github.com/protiumx/blogpub/blob/main/src/parser.ts#L31"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;What other feature would you find useful for this action?&lt;br&gt;
Let me know in the comments.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Publish your blog articles everywhere with this github action</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Sun, 16 Jan 2022 14:35:02 +0000</pubDate>
      <link>https://dev.to/protium/publish-your-blog-articles-everywhere-with-this-github-action-3g6k</link>
      <guid>https://dev.to/protium/publish-your-blog-articles-everywhere-with-this-github-action-3g6k</guid>
      <description>&lt;p&gt;Long ago I made this &lt;a href="https://dev.to/protium/comment/clno"&gt;comment&lt;/a&gt; in this &lt;a href="https://dev.to/maxime1992/manage-your-dev-to-blog-posts-from-a-git-repo-and-use-continuous-deployment-to-auto-publish-update-them-143j"&gt;article&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This is a really good idea with great potential. 
Imagine a standarized API for different blogs. 
You can automatize publishing and editing, multiple collaborators.
And use a git provider as unique source of content.

And you can also make your git repo as some sort of blog.
I'll use it for my next posts for sure.

Thanks!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It was an idea that I had left on the back of my head and I didn't come back to it because I wasn't writing articles actively. But this has changed in the last months and I have published 6 articles during December. So I decided to revisit the idea and finally develop it.&lt;/p&gt;
&lt;h2&gt;
  
  
  blogpub
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/marketplace/actions/blogpub" rel="noopener noreferrer"&gt;blogpub&lt;/a&gt; is a &lt;strong&gt;github action&lt;/strong&gt; developed with &lt;code&gt;typescript&lt;/code&gt; with the purpose of allowing you to use a &lt;code&gt;github&lt;/code&gt; repository as &lt;strong&gt;source of truth&lt;/strong&gt; for your blog posts. The action supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically publishing to &lt;code&gt;Medium&lt;/code&gt; and &lt;code&gt;DEV.to&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Templating&lt;/li&gt;
&lt;li&gt;Metadata/config section (published, tags, title, etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In fact, this article was automatically published by &lt;code&gt;blogpub&lt;/code&gt; and you can see it's source in this &lt;a href="https://github.com/protiumx/blog/tree/main/articles" rel="noopener noreferrer"&gt;folder&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Template Support
&lt;/h2&gt;

&lt;p&gt;Sometimes we want to add different content to our blog posts depending on the platform, for instance for &lt;code&gt;dev.to&lt;/code&gt; I want to use &lt;code&gt;liquid tags&lt;/code&gt;, which &lt;code&gt;medium&lt;/code&gt; doesn't support.&lt;br&gt;
For this purpose I have used &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;handlebars&lt;/a&gt; to make use of conditionals. Example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;{{#if medium}}
This is only for Medium
{{/if}}
{{#if devto}}
This is only for DEV.to
{{/if}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For the first release of the action, the template context contains:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;devto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Article metadata
&lt;/h2&gt;

&lt;p&gt;I have added support for a &lt;strong&gt;metadata&lt;/strong&gt; section (similar to dev.to) where we can specify the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt;: &lt;code&gt;[string]&lt;/code&gt; The title of the article. If not specified, the &lt;strong&gt;first&lt;/strong&gt; H1 heading will be used.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: &lt;code&gt;[string]&lt;/code&gt; Description for &lt;code&gt;dev.to&lt;/code&gt; API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tags&lt;/code&gt;: &lt;code&gt;[string]&lt;/code&gt; Comma separated tags. Note: Medium allows up to 5 tags whereas Dev.to only 4.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;license&lt;/code&gt;: &lt;code&gt;[string]&lt;/code&gt; Medium license type. Refer to &lt;a href="https://github.com/Medium/medium-api-docs#33-posts" rel="noopener noreferrer"&gt;Medium API Docs&lt;/a&gt;. &lt;strong&gt;Default&lt;/strong&gt;: &lt;code&gt;public-domain&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;published&lt;/code&gt;: &lt;code&gt;[boolean]&lt;/code&gt;. &lt;strong&gt;Default&lt;/strong&gt;: &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;First&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;blogpub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;test"&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test, ci&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gh"&gt;# I'm using `blogpub`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Adopting CI/CD practices for blogging
&lt;/h2&gt;

&lt;p&gt;Imagine the following scenario:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write an article&lt;/li&gt;
&lt;li&gt;Create PR&lt;/li&gt;
&lt;li&gt;CI checks the &lt;strong&gt;spelling&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your peers review the article and propose changes or approve it&lt;/li&gt;
&lt;li&gt;Merge the PR to &lt;code&gt;main&lt;/code&gt; and it gets published everywhere&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://github.com/marketplace/actions/send-tweet-action" rel="noopener noreferrer"&gt;send tweet action&lt;/a&gt; to promote your new article.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can imagine this workflow in a company where different colleagues write articles together. I like the idea that we could also automatize tweets, this is why &lt;code&gt;blogpub&lt;/code&gt; outputs the URLs of the article after publishing it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Ideally you want to run this action for every push to your &lt;code&gt;main&lt;/code&gt; branch. This setup should be enough.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;publish'&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main'&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;articles/*'&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish new article&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;    
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;blogpub&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;protiumx/blogpub@v0.2.3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;articles_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;articles&lt;/span&gt;
          &lt;span class="na"&gt;devto_api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.DEV}}&lt;/span&gt;
          &lt;span class="na"&gt;gh_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.GH}}&lt;/span&gt;
          &lt;span class="na"&gt;medium_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.MEDIUM}}&lt;/span&gt;
          &lt;span class="na"&gt;medium_user_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;user_id&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Few things to take into account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We only want to run the action for files inside a folder.&lt;/li&gt;
&lt;li&gt;You need to setup repository secrets with your tokens and api keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check this &lt;a href="https://github.com/protiumx/blog" rel="noopener noreferrer"&gt;blog source&lt;/a&gt; to see this in action.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is missing?
&lt;/h2&gt;

&lt;p&gt;For the first release I just wanted to be able to publish the articles in Medium and DEV.to. But there are a few features that would be handy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Uploading images:&lt;/strong&gt; Imagine also versioning images and then uploading those images automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support for updates:&lt;/strong&gt; If you want to make a correction, it should be possible to update the source and it should get reflected in all the platform the article was published to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform specific configuration&lt;/strong&gt;: maybe we want different tags per platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support multiple articles:&lt;/strong&gt; at the moment &lt;code&gt;blogpub&lt;/code&gt; publishes only 1 article per run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What else would you like to have? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;That's it!&lt;br&gt;
As usual, any help is well received and I have a &lt;a href="https://github.com/protiumx/blogpub#todo" rel="noopener noreferrer"&gt;TODO&lt;/a&gt; list if you would like to collaborate with this project.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/protiumx" rel="noopener noreferrer"&gt;
        protiumx
      &lt;/a&gt; / &lt;a href="https://github.com/protiumx/blogpub" rel="noopener noreferrer"&gt;
        blogpub
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Github action to publish your blog articles from Markdown to Medium or Dev.to
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;blogpub&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/protiumx/blogpub/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/protiumx/blogpub/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Github action to publish your blog articles to &lt;a href="https://medium.com/" rel="nofollow noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="http://dev.to/" rel="nofollow"&gt;Dev.to&lt;/a&gt; using their respective REST APIs
The action searches for markdown files in the commit of the &lt;code&gt;push&lt;/code&gt; event and uses &lt;strong&gt;first&lt;/strong&gt; &lt;code&gt;md&lt;/code&gt; file that finds.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Updating articles&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Currently it's not supported to update the articles on the different platforms.
If the markdown file found in the &lt;code&gt;push&lt;/code&gt; event &lt;strong&gt;already&lt;/strong&gt; exists on the commit &lt;strong&gt;before&lt;/strong&gt;, the action will &lt;strong&gt;skip it&lt;/strong&gt;.
This avoids publishing the article again.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Pre-requisites&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;In order to interact with both platforms API's you will need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Medium&lt;/strong&gt; &lt;a href="https://github.com/Medium/medium-api-docs#21-self-issued-access-tokens" rel="noopener noreferrer"&gt;integration token&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev.to&lt;/strong&gt; &lt;a href="https://developers.forem.com/api#section/Authentication" rel="nofollow noopener noreferrer"&gt;API Key&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token" rel="noopener noreferrer"&gt;Github access token&lt;/a&gt; with &lt;code&gt;reading&lt;/code&gt; permissions&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The action will grab an &lt;code&gt;markdown&lt;/code&gt; file from a push event to a branch.
The following workflow configuration will publish articles that are committed to
the &lt;code&gt;main&lt;/code&gt; branch:&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;on&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;push&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;branches&lt;/span&gt;:
      - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;main&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
    &lt;span class="pl-ent"&gt;paths&lt;/span&gt;:
      - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;articles/*&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/protiumx/blogpub" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;🤖&lt;/p&gt;

</description>
      <category>ci</category>
      <category>github</category>
      <category>typescript</category>
      <category>blogging</category>
    </item>
    <item>
      <title>autoenv: relax, ansible will setup your dev environment for you</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Thu, 30 Dec 2021 16:54:59 +0000</pubDate>
      <link>https://dev.to/protium/autoenv-relax-ansible-will-setup-your-dev-environment-for-you-4j7d</link>
      <guid>https://dev.to/protium/autoenv-relax-ansible-will-setup-your-dev-environment-for-you-4j7d</guid>
      <description>&lt;p&gt;Yesterday I published: &lt;a href="https://dev.to/protium/bash-gnu-stow-take-a-walk-while-your-new-macbook-is-being-set-up-p1o"&gt;Bash + GNU Stow: take a walk while your new macbook is being set up&lt;/a&gt;. I was already taking a look at &lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;ansible&lt;/a&gt; so I decided to finish my playbooks to match exactly what my &lt;a href="https://github.com/protiumx/.dotfiles" rel="noopener noreferrer"&gt;dotfiles&lt;/a&gt; script does.&lt;/p&gt;

&lt;p&gt;So today we are going to learn how to automate our dev environment with &lt;code&gt;ansible&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forjvlcjog3awm18ggt8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forjvlcjog3awm18ggt8q.png" alt="intro"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrapping
&lt;/h2&gt;

&lt;p&gt;Similar to my &lt;code&gt;dotfiles&lt;/code&gt; script, we need to install necessary dependencies before running the &lt;strong&gt;playbooks&lt;/strong&gt;. For that we need these dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;xcode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brew&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git&lt;/code&gt;, &lt;code&gt;python&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Putting into code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bootstrap&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  info &lt;span class="s2"&gt;"Bootstraping..."&lt;/span&gt;
  info &lt;span class="s2"&gt;"Installing xcode"&lt;/span&gt;
  install_xcode

  info &lt;span class="s2"&gt;"Installing HomeBrew"&lt;/span&gt;
  install_homebrew

  info &lt;span class="s2"&gt;"Installing python3"&lt;/span&gt;
  install_brew_formula &lt;span class="s2"&gt;"python3"&lt;/span&gt;

  info &lt;span class="s2"&gt;"Installing git"&lt;/span&gt;
  install_brew_formula &lt;span class="s2"&gt;"git"&lt;/span&gt;

  &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bin:&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/opt/homebrew/bin/python3 &lt;span class="nt"&gt;-m&lt;/span&gt; site &lt;span class="nt"&gt;--user-base&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;PATH

  info &lt;span class="s2"&gt;"Installing pip"&lt;/span&gt;
  curl https://bootstrap.pypa.io/get-pip.py | python3
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Installing ansible
&lt;/h2&gt;

&lt;p&gt;I'm using &lt;a href="https://virtualenv.pypa.io/en/latest/" rel="noopener noreferrer"&gt;virtualenv&lt;/a&gt; to fetch all &lt;code&gt;ansible&lt;/code&gt; dependencies, without installing them globally.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; virtualenv
virtualenv venv
&lt;span class="nb"&gt;.&lt;/span&gt; venv/bin/activate

info &lt;span class="s2"&gt;"Installing Ansible"&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;ansible

info &lt;span class="s2"&gt;"Setting up Ansible"&lt;/span&gt;
ansible-galaxy collection &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; setup/requirements.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Requirements are:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;community.crypto&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;community.general&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The collection &lt;code&gt;community.crypto&lt;/code&gt; will be used to generate &lt;strong&gt;SSH keys&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At this point we have all our requirements. Let's write the playbook.&lt;/p&gt;
&lt;h2&gt;
  
  
  autoenv playbook
&lt;/h2&gt;

&lt;p&gt;To replicate my &lt;code&gt;.dotfiles&lt;/code&gt; commands, I have set up the following &lt;strong&gt;tasks:&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  [+] Installing all the apps, &lt;code&gt;brew&lt;/code&gt; packages and casks
&lt;/h3&gt;

&lt;p&gt;For this we can use the &lt;code&gt;community.general&lt;/code&gt;. We can define some list &lt;strong&gt;variables&lt;/strong&gt; with all the apps/packages/apps we use. Then we execute as follows:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Homebrew formulas&lt;/span&gt;
  &lt;span class="na"&gt;community.general.homebrew&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;homebrew['formulas']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;homebrew_formulas&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Same for &lt;code&gt;taps&lt;/code&gt; and &lt;code&gt;casks&lt;/code&gt;.&lt;br&gt;
For App store apps we use &lt;a href="https://github.com/mas-cli/mas" rel="noopener noreferrer"&gt;mas&lt;/a&gt; and &lt;code&gt;ansible.builtin.command&lt;/code&gt; to run a shell command in &lt;strong&gt;loop:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install app store apps&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mas&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;homebrew['mas']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;mac_app_store&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  [+] Writing all my &lt;strong&gt;macOS&lt;/strong&gt; settings
&lt;/h3&gt;

&lt;p&gt;Here we will need &lt;code&gt;community.general.osx_defaults&lt;/code&gt;. We need to define a list with settings, having &lt;code&gt;domain&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt; and type for each setting. E.g.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.apple.TimeMachine&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DoNotOfferNewDisksForBackup&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Disable prompting to use new exteral drives as Time Machine volume&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bool&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After defining all our settings, this task is defined as follows:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set macOS default settings&lt;/span&gt;
  &lt;span class="na"&gt;community.general.osx_defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item['domain']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item['key']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item['type']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;default(omit)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item['value']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;defaults&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;system_settings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  [+] Stowing dotfiles
&lt;/h3&gt;

&lt;p&gt;I've seen many setups with &lt;code&gt;ansible&lt;/code&gt; using the &lt;code&gt;file copy&lt;/code&gt; functionality to manag &lt;code&gt;dotfiles&lt;/code&gt;, but this is not suitable for me. I prefer using &lt;a href="https://www.gnu.org/software/stow/manual/stow.html" rel="noopener noreferrer"&gt;stow&lt;/a&gt; so any change on the &lt;strong&gt;symlinks&lt;/strong&gt; can be pushed to the &lt;code&gt;git&lt;/code&gt; repo.&lt;/p&gt;

&lt;p&gt;I opted for using the same &lt;strong&gt;bash script&lt;/strong&gt; from my &lt;strong&gt;dotfiles&lt;/strong&gt; repo and call the script as a command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Dotfiles&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sh ~/.autoenv/dotfiles/install.sh&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotfiles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  [+] Setting &lt;code&gt;vs code&lt;/code&gt; as default for all the source code extensions
&lt;/h3&gt;

&lt;p&gt;For this I just needed to set a list &lt;code&gt;variable&lt;/code&gt; with all the extension and then loop into a &lt;code&gt;shell&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set VSCode as default editor&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;local exts=("{{ fileExtensions | join(' ') }}")&lt;/span&gt;
    &lt;span class="s"&gt;for ext in $exts; do&lt;/span&gt;
    &lt;span class="s"&gt;duti -s com.microsoft.VSCode $ext all&lt;/span&gt;
    &lt;span class="s"&gt;done&lt;/span&gt;
    &lt;span class="s"&gt;exit 0&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;settings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  [+] Installing &lt;code&gt;vim-plug&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Same as before, another &lt;code&gt;shell&lt;/code&gt; command will do the trick.&lt;br&gt;
But here I also wanted to check if &lt;code&gt;vim-plug&lt;/code&gt; was already installed. And for that &lt;code&gt;ansible&lt;/code&gt; provides a &lt;strong&gt;file stat&lt;/strong&gt; api and &lt;code&gt;conditional&lt;/code&gt; tasks using the &lt;code&gt;when&lt;/code&gt; keyword.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check vim-plugged file&lt;/span&gt;
  &lt;span class="na"&gt;stat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.local/share/nvim/site/autoload/plug.vim&lt;/span&gt;
    &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vim_plug&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install neovim plugin manager&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sh -c 'curl -fLo $HOME/.local/share/nvim/site/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'&lt;/span&gt;
  &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;not vim_plug.stat.exists&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  [+] Generating SSH keys
&lt;/h3&gt;

&lt;p&gt;As mentioned before, here we make use of the &lt;code&gt;community.crypto&lt;/code&gt; collection to generate a &lt;strong&gt;ssh key&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create SSH keys&lt;/span&gt;
  &lt;span class="na"&gt;community.crypto.openssh_keypair&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user_dir&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/.ssh/id_{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;passphrase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ssh_passphrase&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4096&lt;/span&gt;
    &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user_id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}@{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_date_time['date']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ssh_key_types_to_generate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;split(',')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;loop_control&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;ssh_passphrase&lt;/code&gt; and &lt;code&gt;ssh_key_types_to_generate&lt;/code&gt; are variables.&lt;/p&gt;
&lt;h3&gt;
  
  
  [+] Uploading keys to &lt;strong&gt;github&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In this task I want to &lt;strong&gt;upload&lt;/strong&gt; my &lt;strong&gt;ssh key&lt;/strong&gt; to github. For that I need a &lt;strong&gt;github token&lt;/strong&gt; and use the &lt;code&gt;community.general&lt;/code&gt; collection:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Register SSH key with Github&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;github_keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user_dir&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/.ssh/id_ed25519.pub"&lt;/span&gt;
    &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('first_found',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github_keys,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;errors='ignore')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;community.general.github_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user_id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}@{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('file',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pubkey)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github['personal_token']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  [+] Installing pip packages
&lt;/h3&gt;

&lt;p&gt;To install our &lt;code&gt;pip&lt;/code&gt; packages we can use the &lt;code&gt;ansible.buitin.pip&lt;/code&gt; command and declare a list &lt;strong&gt;variable&lt;/strong&gt; with the packages to install:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Python packages&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.pip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;executable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pip&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pypi_packages&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;extra_args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--user&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;core&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Post install
&lt;/h2&gt;

&lt;p&gt;So I had a few things left off that weren't suitable to run with &lt;code&gt;ansible&lt;/code&gt; since they need some interaction: running &lt;code&gt;rustup-init&lt;/code&gt;, installing &lt;code&gt;nvim&lt;/code&gt; plugins and restarting the system. Basically:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvim +PlugInstall +qall

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;hash &lt;/span&gt;rustc &amp;amp;&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;info &lt;span class="s2"&gt;"Triggering Rust-up"&lt;/span&gt;
  rustup-init
&lt;span class="k"&gt;fi

&lt;/span&gt;info &lt;span class="s2"&gt;"Done"&lt;/span&gt;
info &lt;span class="s2"&gt;"System must restart. Restart?"&lt;/span&gt;

&lt;span class="k"&gt;select &lt;/span&gt;yn &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"y"&lt;/span&gt; &lt;span class="s2"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  case&lt;/span&gt; &lt;span class="nv"&gt;$yn&lt;/span&gt; &lt;span class="k"&gt;in
      &lt;/span&gt;y &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;shutdown &lt;span class="nt"&gt;-r&lt;/span&gt; now&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
      n &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We are completely done 🤖&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I decided to try &lt;code&gt;ansible&lt;/code&gt; out of curiosity to manage my dev environment. It is a very robust and versatile solution &lt;strong&gt;but&lt;/strong&gt; it feels like an overkill for this task. I definitely like to set configuration files in &lt;code&gt;yaml&lt;/code&gt; but my configurations/variables inside &lt;code&gt;bash&lt;/code&gt; files are not too bloated to justify the switch (though I wrote the whole thing for ansible already). For now I'd prefer to continue using my &lt;a href="https://github.com/protiumx/.dotfiles" rel="noopener noreferrer"&gt;bash+stow&lt;/a&gt; solution.&lt;/p&gt;

&lt;p&gt;What do you think? Would you rather use &lt;code&gt;ansible&lt;/code&gt;?&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/protiumx" rel="noopener noreferrer"&gt;
        protiumx
      &lt;/a&gt; / &lt;a href="https://github.com/protiumx/autoenv" rel="noopener noreferrer"&gt;
        autoenv
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Setup new dev machines with Ansible
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;autoenv&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;My &lt;a href="https://www.ansible.com/" rel="nofollow noopener noreferrer"&gt;ansible&lt;/a&gt; playbooks to setup macOS laptos with a development environment.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Running&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;export&lt;/span&gt; GITHUB_TOKEN=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&amp;lt;- token -&amp;gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
curl -sO https://raw.githubusercontent.com/protiumx/autoenv/main/autoenv&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The script will install the initial requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;xCode&lt;/li&gt;
&lt;li&gt;Hombrew&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Python3 and PIP&lt;/li&gt;
&lt;li&gt;Virtual env&lt;/li&gt;
&lt;li&gt;Ansible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From there, &lt;code&gt;ansible&lt;/code&gt; takes over with the &lt;a href="https://github.com/protiumx/autoenv./playbooks/autoenv.yml" rel="noopener noreferrer"&gt;autoenv&lt;/a&gt; playbook.
When &lt;code&gt;ansible&lt;/code&gt; is done, the &lt;a href="https://github.com/protiumx/autoenv./post-install.sh" rel="noopener noreferrer"&gt;post-install&lt;/a&gt; script run commands that are not suitable for &lt;code&gt;ansible&lt;/code&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Github Token&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;ansible&lt;/code&gt; will upload the ssh key to github, for that you need to export a &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; before running the scripts.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Customization&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Most of the customizable configs reside on the &lt;a href="https://github.com/protiumx/autoenv./playbooks/group_vars/all" rel="noopener noreferrer"&gt;grou-vars&lt;/a&gt; definitions.
You can check all the system settings, &lt;code&gt;brew&lt;/code&gt; packages/casks and app store apps that will be installed.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/protiumx/autoenv" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbqeni2klo77w1cerfe2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbqeni2klo77w1cerfe2.png" alt="ansi"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👽&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>productivity</category>
      <category>automation</category>
      <category>macos</category>
    </item>
    <item>
      <title>Bash + GNU Stow: take a walk while your new macbook is being set up</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Wed, 29 Dec 2021 14:06:43 +0000</pubDate>
      <link>https://dev.to/protium/bash-gnu-stow-take-a-walk-while-your-new-macbook-is-being-set-up-p1o</link>
      <guid>https://dev.to/protium/bash-gnu-stow-take-a-walk-while-your-new-macbook-is-being-set-up-p1o</guid>
      <description>&lt;p&gt;I'm addicted to automation. I said it. Okay?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s6j9uCOc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media0.giphy.com/media/l1KtYG8BndKBmWrM4/200.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s6j9uCOc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media0.giphy.com/media/l1KtYG8BndKBmWrM4/200.gif" alt="auto" width="266" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today we are going to learn how we can automatize all our macOS settings, apps and &lt;code&gt;homebrew&lt;/code&gt; packages using &lt;code&gt;bash&lt;/code&gt; and &lt;a href="https://www.gnu.org/software/stow/manual/stow.html"&gt;stow&lt;/a&gt; (and a few other cli tools).&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;As developers we might encounter ourselves setting up a new &lt;strong&gt;macOS&lt;/strong&gt; environment quite often (I had to do it 2 times so far this year). We tend to accumulate &lt;code&gt;dotfiles&lt;/code&gt; pretty easily and keeping them in sync across multiple machines becomes painful.&lt;br&gt;
And not only that. We also accumulate a galaxy of different packages, tools and apps that we use on daily bases to work.&lt;/p&gt;

&lt;p&gt;Oh and all the macOS settings that we surely need? our poor souls.&lt;/p&gt;

&lt;p&gt;In order to reduce the pain and enjoy life we can make use of different tools and our coding skills to automatize all this process.&lt;/p&gt;
&lt;h2&gt;
  
  
  The objective
&lt;/h2&gt;

&lt;p&gt;When I receive a new &lt;strong&gt;Macbook&lt;/strong&gt; at a new job, all I want to do is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login in the app store&lt;/li&gt;
&lt;li&gt;Download and run a &lt;em&gt;magic&lt;/em&gt; script&lt;/li&gt;
&lt;li&gt;Go out and enjoy the sun while the macbook is being set up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0WrLFCze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48lpduzjm3xgmws21n9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0WrLFCze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48lpduzjm3xgmws21n9c.png" alt="Image description" width="800" height="600"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Bootstraping
&lt;/h2&gt;

&lt;p&gt;So first we need a few tools to start setting up everything. As we already know &lt;code&gt;xcode&lt;/code&gt;, &lt;code&gt;brew&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; are a must. Here we start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcode-select &lt;span class="nt"&gt;--install&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;xcodebuild &lt;span class="nt"&gt;-license&lt;/span&gt; accept

/bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Homebrew/install/master/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

brew &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Brew taps, casks and formulas
&lt;/h2&gt;

&lt;p&gt;We want to install &lt;strong&gt;all the packages and apps we need&lt;/strong&gt;.&lt;br&gt;
To list all your currently installed packages (formulae) and apps (casks) you can run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;we also need to list all the &lt;code&gt;taps&lt;/code&gt; (Third-Party Repositories) we use:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Perfect. With this information we can code some &lt;code&gt;for loops&lt;/code&gt; to iterate on the &lt;code&gt;taps&lt;/code&gt;, &lt;code&gt;casks&lt;/code&gt; and &lt;code&gt;formulas&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apply_brew_taps&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;tap_packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;tap &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$tap_packages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;brew tap | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tap&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;warn &lt;span class="s2"&gt;"Tap &lt;/span&gt;&lt;span class="nv"&gt;$tap&lt;/span&gt;&lt;span class="s2"&gt; is already applied"&lt;/span&gt;
    &lt;span class="k"&gt;else
      &lt;/span&gt;brew tap &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tap&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

install_brew_formulas&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;formulas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;formula &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$formulas&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;brew list &lt;span class="nt"&gt;--formula&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$formula&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;warn &lt;span class="s2"&gt;"Formula &lt;/span&gt;&lt;span class="nv"&gt;$formula&lt;/span&gt;&lt;span class="s2"&gt; is already installed"&lt;/span&gt;
    &lt;span class="k"&gt;else
      &lt;/span&gt;info &lt;span class="s2"&gt;"Installing package &amp;lt; &lt;/span&gt;&lt;span class="nv"&gt;$formula&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;"&lt;/span&gt;
      brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$formula&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
  done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

install_brew_casks&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;casks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;cask &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$casks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;brew list &lt;span class="nt"&gt;--casks&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cask&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;warn &lt;span class="s2"&gt;"Cask &lt;/span&gt;&lt;span class="nv"&gt;$cask&lt;/span&gt;&lt;span class="s2"&gt; is already installed"&lt;/span&gt;
    &lt;span class="k"&gt;else
      &lt;/span&gt;info &lt;span class="s2"&gt;"Installing cask &amp;lt; &lt;/span&gt;&lt;span class="nv"&gt;$cask&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;"&lt;/span&gt;
      brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cask&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
  done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; in order to keep our scripts some how &lt;strong&gt;idempotent&lt;/strong&gt;, we want to check if the formulas/casks/taps are already installed.&lt;/p&gt;

&lt;p&gt;These functions will take a list and run &lt;code&gt;brew install&lt;/code&gt;. How do we use them?&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# apply the taps first&lt;/span&gt;
&lt;span class="nv"&gt;taps&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;homebrew/cask&lt;span class="o"&gt;)&lt;/span&gt;
apply_brew_taps &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;taps&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# install casks&lt;/span&gt;
&lt;span class="nv"&gt;apps&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;firefox docker&lt;span class="o"&gt;)&lt;/span&gt;
install_brew_casks &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# install formulas&lt;/span&gt;
&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;curl go&lt;span class="o"&gt;)&lt;/span&gt;
install_brew_formulas &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;OK. But some of my apps are not available in hombrew&lt;/strong&gt; 😡&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing App Store apps
&lt;/h2&gt;

&lt;p&gt;For all the apps that cannot be found as &lt;code&gt;brew casks&lt;/code&gt; we can use &lt;a href="https://github.com/mas-cli/mas"&gt;mas (Mac App Store cli)&lt;/a&gt;.&lt;br&gt;
This cli tool needs the &lt;strong&gt;IDs&lt;/strong&gt; of the apps. To check the apps you have installed simply run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mas list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If not all your apps are listed you can also &lt;strong&gt;search&lt;/strong&gt; for them and get their IDs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mas search Goodnotes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So again, writing some &lt;strong&gt;pretty code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;masApps&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"937984704"&lt;/span&gt;   &lt;span class="c"&gt;# Amphetamine&lt;/span&gt;
  &lt;span class="s2"&gt;"1444383602"&lt;/span&gt;  &lt;span class="c"&gt;# Good Notes 5&lt;/span&gt;
  &lt;span class="s2"&gt;"768053424"&lt;/span&gt;   &lt;span class="c"&gt;# Gappling (svg viewer)&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

install_masApps&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  info &lt;span class="s2"&gt;"Installing App Store apps..."&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;app &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$masApps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;mas &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Neat!&lt;/p&gt;

&lt;p&gt;So far we have installed all our &lt;strong&gt;apps&lt;/strong&gt; and &lt;strong&gt;cli tools&lt;/strong&gt;.&lt;br&gt;
Let's see how we can automatize our &lt;strong&gt;macOS settings&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;defaults&lt;/code&gt;: macOS settings from the terminal
&lt;/h2&gt;

&lt;p&gt;This tool will help us to &lt;strong&gt;read&lt;/strong&gt; and &lt;strong&gt;write&lt;/strong&gt; settings. For that, you need to know the &lt;strong&gt;domain&lt;/strong&gt;, &lt;strong&gt;key&lt;/strong&gt; and &lt;strong&gt;type&lt;/strong&gt; of the setting. Usually a &lt;strong&gt;google/duckduckgo/ecosia&lt;/strong&gt; search should help you to find this information. If not, &lt;code&gt;defaults&lt;/code&gt; provides some commands to help you &lt;strong&gt;search&lt;/strong&gt; for the settings.&lt;br&gt;
Let's say I want to change some &lt;strong&gt;finder&lt;/strong&gt; setting. First, find its domain:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;defaults domains | &lt;span class="nb"&gt;grep &lt;/span&gt;finder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Read all the &lt;strong&gt;settings available&lt;/strong&gt; for &lt;code&gt;finder&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;defaults &lt;span class="nb"&gt;read &lt;/span&gt;com.apple.finder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I want to change &lt;strong&gt;ShowExternalHardDrivesOnDesktop&lt;/strong&gt; but I don't know its &lt;strong&gt;type&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;defaults read-type com.apple.finder ShowExternalHardDrivesOnDesktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Type is boolean&lt;/code&gt;. So finally we can set this to false&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  defaults write com.apple.finder ShowExternalHardDrivesOnDesktop &lt;span class="nt"&gt;-bool&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;(You can see all my settings in my &lt;strong&gt;dotfiles&lt;/strong&gt; repo,link below)&lt;/p&gt;

&lt;p&gt;Lastly, and this is a personal preference, I want to set &lt;code&gt;vscode&lt;/code&gt; as default application for all my source code files. To do so we will use &lt;a href="https://github.com/moretension/duti"&gt;duti&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code_as_default_text_editor&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="s2"&gt;".c"&lt;/span&gt;
    &lt;span class="s2"&gt;".cpp"&lt;/span&gt;
    &lt;span class="s2"&gt;".js"&lt;/span&gt;
    &lt;span class="s2"&gt;".jsx"&lt;/span&gt;
    &lt;span class="s2"&gt;".ts"&lt;/span&gt;
    &lt;span class="s2"&gt;".tsx"&lt;/span&gt;
    &lt;span class="s2"&gt;".json"&lt;/span&gt;
    &lt;span class="s2"&gt;".md"&lt;/span&gt;
    &lt;span class="s2"&gt;".sql"&lt;/span&gt;
    &lt;span class="s2"&gt;".html"&lt;/span&gt;
    &lt;span class="s2"&gt;".css"&lt;/span&gt;
    &lt;span class="s2"&gt;".scss"&lt;/span&gt;
    &lt;span class="s2"&gt;".sass"&lt;/span&gt;
    &lt;span class="s2"&gt;".py"&lt;/span&gt;
    &lt;span class="s2"&gt;".sum"&lt;/span&gt;
    &lt;span class="s2"&gt;".rs"&lt;/span&gt;
    &lt;span class="s2"&gt;".go"&lt;/span&gt;
    &lt;span class="s2"&gt;".sh"&lt;/span&gt;
    &lt;span class="s2"&gt;".log"&lt;/span&gt;
    &lt;span class="s2"&gt;".toml"&lt;/span&gt;
    &lt;span class="s2"&gt;".yml"&lt;/span&gt;
    &lt;span class="s2"&gt;".yaml"&lt;/span&gt;
    &lt;span class="s2"&gt;"public.plain-text"&lt;/span&gt;
    &lt;span class="s2"&gt;"public.unix-executable"&lt;/span&gt;
    &lt;span class="s2"&gt;"public.data"&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;ext &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;duti &lt;span class="nt"&gt;-s&lt;/span&gt; com.microsoft.VSCode &lt;span class="nv"&gt;$ext&lt;/span&gt; all
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Settings:&lt;/strong&gt; check.&lt;/p&gt;
&lt;h2&gt;
  
  
  Managing dotfiles with GNU Stow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;stow&lt;/strong&gt; is simply a &lt;strong&gt;symlink manager&lt;/strong&gt; that let us define a folder structure (package) for our dotfiles and then &lt;strong&gt;symlink&lt;/strong&gt; them following that structure. So let's say one of your dotfiles is &lt;code&gt;~/.config/kitty/kitty.conf&lt;/code&gt;, this will be our &lt;code&gt;kitty&lt;/code&gt; &lt;strong&gt;package&lt;/strong&gt;. In order to place this file in the correct path, we define the package as as&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotfiles/kitty/.config/kitty/kitty.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;(assuming all our dotfiles are in a &lt;strong&gt;folder&lt;/strong&gt; called dotfiles)&lt;br&gt;
We can &lt;strong&gt;simulate&lt;/strong&gt; stow to see the resulting symlinks&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;dotfiles
stow &lt;span class="nt"&gt;-nSv&lt;/span&gt; kitty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If the output looks correct, we symlink the package with&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;stow &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt; kitty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; we want to use stow only for &lt;strong&gt;files&lt;/strong&gt;, since a folder like &lt;code&gt;.config&lt;/code&gt; will always contain many other files/folders, &lt;strong&gt;we don't want to symlink this folder&lt;/strong&gt;. Therefor we need to first remove existing files (the ones we want to stow) and check that the needed directories exist. So, some lovely code for that:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;stow_dotfiles&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="s2"&gt;".zprofile"&lt;/span&gt;
    &lt;span class="s2"&gt;".gitconfig"&lt;/span&gt;
    &lt;span class="s2"&gt;".zshrc"&lt;/span&gt;
    &lt;span class="s2"&gt;".vimrc"&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;folders&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="s2"&gt;".config/nvim"&lt;/span&gt;
    &lt;span class="s2"&gt;".config/kitty"&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt;
  info &lt;span class="s2"&gt;"Removing existing config files"&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
  &lt;/span&gt;&lt;span class="k"&gt;done

  for &lt;/span&gt;d &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$folders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;done

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;dotfiles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"kitty nvim"&lt;/span&gt;
  info &lt;span class="s2"&gt;"Stowing: &lt;/span&gt;&lt;span class="nv"&gt;$dotfiles&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  stow &lt;span class="nt"&gt;-d&lt;/span&gt; stow &lt;span class="nt"&gt;--verbose&lt;/span&gt; 1 &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt; &lt;span class="nv"&gt;$dotfiles&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So far so good, all our dotfiles are &lt;strong&gt;versioned&lt;/strong&gt; and any change on them can be pushed and synced in all our macOS machines with &lt;strong&gt;git&lt;/strong&gt;.&lt;br&gt;
What else did I want to do?&lt;br&gt;
...&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up Oh My Zsh
&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="https://github.com/ohmyzsh/ohmyzsh"&gt;oh my zsh&lt;/a&gt; along with &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;powerlevel10k&lt;/a&gt; and this is how my terminal looks&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QBnmTMFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qyblg89lvg992ftu1qlg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QBnmTMFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qyblg89lvg992ftu1qlg.png" alt="terminal" width="880" height="356"&gt;&lt;/a&gt;&lt;br&gt;
pretty, right?&lt;/p&gt;

&lt;p&gt;So I need to install this scripts on my system, for which I have more lovely code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;install_oh_my_zsh&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.zshrc &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;info &lt;span class="s2"&gt;"Installing oh my zsh..."&lt;/span&gt;
    &lt;span class="nv"&gt;ZSH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.oh-my-zsh &lt;span class="nv"&gt;ZSH_DISABLE_COMPFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;chmod &lt;/span&gt;744 ~/.oh-my-zsh/oh-my-zsh.sh

    info &lt;span class="s2"&gt;"Installing powerlevel10k"&lt;/span&gt;
    git clone &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/romkatv/powerlevel10k.git ~/.oh-my-zsh/custom/themes/powerlevel10k
  &lt;span class="k"&gt;else
    &lt;/span&gt;warn &lt;span class="s2"&gt;"oh-my-zsh already installed"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; my &lt;code&gt;powerlevel10k&lt;/code&gt; configuration is part of my dotfiles. If you don't have any, you can run &lt;code&gt;p10k configure&lt;/code&gt; and follow the configuration process.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing (Neo)Vim plugins
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pzWkhkLL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ib1ygwkr2xd85m3goqyb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pzWkhkLL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ib1ygwkr2xd85m3goqyb.png" alt="Image description" width="458" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, I also want to automatically install all my neovim plugins. First I need to install the &lt;strong&gt;plugin manager&lt;/strong&gt; (I use &lt;a href="https://github.com/junegunn/vim-plug"&gt;vim-plug&lt;/a&gt;) and then I want to install all the plugins defined on my dotfiles&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;install_neovim&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  info &lt;span class="s2"&gt;"Installing NeoVim"&lt;/span&gt;
  install_brew_formulas neovim

  info &lt;span class="s2"&gt;"Installing Vim Plugged"&lt;/span&gt;
  sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Install plugins and quit&lt;/span&gt;
nvim +PlugInstall +qall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Eureka. My &lt;strong&gt;dev environment&lt;/strong&gt; is (almost) fully automated. So coming back to the objectives, this is the &lt;em&gt;magic&lt;/em&gt; script:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sO&lt;/span&gt; https://raw.githubusercontent.com/protiumx/.dotfiles/main/dotfiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it. My life quality has been improved by 2Π%.&lt;/p&gt;

&lt;p&gt;And here my dotfiles repo&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/protiumx"&gt;
        protiumx
      &lt;/a&gt; / &lt;a href="https://github.com/protiumx/.dotfiles"&gt;
        .dotfiles
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      My automatic dev machine setup scripts and configs
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
dotfiles&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://github.com/protiumx/.dotfiles/actions/workflows/shell.yml"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RtB4SuzC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/protiumx/.dotfiles/actions/workflows/shell.yml/badge.svg" alt="shell"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Set up dev environment in a &lt;strong&gt;macOS&lt;/strong&gt; machine.
This script installs all the packages and apps I use, &lt;a href="https://www.gnu.org/software/stow/" rel="nofollow"&gt;stow&lt;/a&gt; my dotfiles and sets all my preffered macOS configurations.&lt;/p&gt;
&lt;p&gt;Check my &lt;a href="https://medium.com/@protiumx/bash-gnu-stow-take-a-walk-while-your-new-macbook-is-being-set-up-351a6f2f9225" rel="nofollow"&gt;Medium article&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
Installing&lt;/h2&gt;
&lt;p&gt;Run the &lt;code&gt;dotfiles&lt;/code&gt; script:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;curl -sO https://raw.githubusercontent.com/protiumx/.dotfiles/main/dotfiles&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
Reusing&lt;/h2&gt;
&lt;p&gt;In order to reuse these scripts, here a summary of files you can change/adapt to your needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;scripts/packages.sh&lt;/code&gt;: all the &lt;code&gt;homebrew&lt;/code&gt; taps and packages to install&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts/fonts.sh&lt;/code&gt;: &lt;code&gt;homebrew&lt;/code&gt; fonts to install&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts/apps.sh&lt;/code&gt;: &lt;code&gt;homebrew&lt;/code&gt; casks to install&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts/osx.sh&lt;/code&gt;: &lt;strong&gt;macOS&lt;/strong&gt; settings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts/config.sh&lt;/code&gt;: general settings and dotfiles&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Testing Stow&lt;/h2&gt;
&lt;p&gt;To double checks that the dot files will be correctly linked, you can use stow to simulate the result. E.g.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;stow -nSv vim
&lt;/pre&gt;

&lt;/div&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/protiumx/.dotfiles"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I have added a lot of &lt;code&gt;read -p&lt;/code&gt; on my script to debug each section but they can all be removed so there is no need for interaction. Prompting for the &lt;strong&gt;sudo&lt;/strong&gt; password could be than an enable it for the rest of command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'm missing?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;configuration for different apps like &lt;code&gt;clipy&lt;/code&gt;. &lt;code&gt;defaults&lt;/code&gt; show some configuration but not all of them.&lt;/li&gt;
&lt;li&gt;some macOS config like &lt;code&gt;disable autocapitalization&lt;/code&gt; don't seem to be available through &lt;code&gt;defaults&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd love to hear some ideas to improve this current setup!&lt;br&gt;
Currently I'm checking &lt;a href="https://www.ansible.com/"&gt;ansible&lt;/a&gt; so stay tuned for a possible upgrade.&lt;/p&gt;

&lt;p&gt;👽&lt;/p&gt;

</description>
      <category>automatization</category>
      <category>macos</category>
      <category>bash</category>
      <category>dotfiles</category>
    </item>
    <item>
      <title>Your new pretty and minimalist resume with LaTex</title>
      <dc:creator>protium</dc:creator>
      <pubDate>Tue, 28 Dec 2021 02:01:06 +0000</pubDate>
      <link>https://dev.to/protium/your-new-pretty-and-minimalist-resume-with-latex-421j</link>
      <guid>https://dev.to/protium/your-new-pretty-and-minimalist-resume-with-latex-421j</guid>
      <description>&lt;p&gt;Hi everyone, today we are going to take a look at how we can create a pretty resume using &lt;strong&gt;LaTex&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimalist and concise
&lt;/h2&gt;

&lt;p&gt;When writing a &lt;a href="https://en.wikipedia.org/wiki/R%C3%A9sum%C3%A9" rel="noopener noreferrer"&gt;résumé&lt;/a&gt; we always want to achieve a &lt;strong&gt;minimalist&lt;/strong&gt; and &lt;strong&gt;concise&lt;/strong&gt; document to easily catch the eyes of any recruiter/company. To do so, we want to organize its content into 2 columns, having a good spacing for all the content to fit without losing readability. And here is where &lt;code&gt;LaTex&lt;/code&gt; comes to the rescue. &lt;/p&gt;

&lt;p&gt;I split my resume into: &lt;strong&gt;Skills&lt;/strong&gt; (33%) and &lt;strong&gt;Work Experience&lt;/strong&gt; (66%) columns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="nt"&gt;\begin{minipage}&lt;/span&gt;[t]&lt;span class="p"&gt;{&lt;/span&gt;0.33&lt;span class="k"&gt;\textwidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="nt"&gt;\begin{minipage}&lt;/span&gt;[t]&lt;span class="p"&gt;{&lt;/span&gt;0.66&lt;span class="k"&gt;\textwidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Preview
&lt;/h2&gt;

&lt;p&gt;Before describing each section, let's have a look at the final result&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6szg8kayl7j3dqwvyssc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6szg8kayl7j3dqwvyssc.png" alt="preview"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  # Name Section
&lt;/h2&gt;

&lt;p&gt;This is a command that takes 2 parameters: first name and last name. The last name has a &lt;strong&gt;thicker&lt;/strong&gt; font and uses the &lt;strong&gt;accent&lt;/strong&gt; color.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\namesection&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Alan&lt;span class="p"&gt;}{&lt;/span&gt;Turing&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  # Skills
&lt;/h2&gt;

&lt;p&gt;Here you want to list all your skills and additional information. As you can see in the preview, this column has several &lt;strong&gt;sections&lt;/strong&gt;: Info, Education, Course Work, Skills, and Languages.&lt;/p&gt;
&lt;h3&gt;
  
  
  \ Info
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\section&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Info&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here we place general information: location, date of birth, contact information, &lt;strong&gt;links&lt;/strong&gt;. I think having plain urls looks &lt;strong&gt;pretty ugly&lt;/strong&gt; so I decided to follow the following format: &lt;code&gt;{domain}//{user_name}&lt;/code&gt;. This way it's easier to have an idea of what the link is about. E.g.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\textbf&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Dev.to://&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;\href&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;https://dev.to/protium&lt;span class="p"&gt;}{&lt;/span&gt;protium&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;\\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  \ Education
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\section&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Education&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For this section I opted for the following format, where each entry is a &lt;code&gt;subsection&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{University Acronym}
{University Name}
{Degree}
{Year} | {State, Country}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;E.g.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\subsection&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;FaMAF&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\location&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;National University Of Córdoba&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\descript&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;BSc. in Computer Science &lt;span class="k"&gt;\\&lt;/span&gt;
BSc. in Physics&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\location&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
2017-2019 | Córdoba, Argentina
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  \ Course work
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\section&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Course Work&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A list of all the courses you have finished or certifications you have obtained so far. Here you want to add a link to your certificate if possible (Coursera, Udemy, EdX, etc). If there is no certificate you could add a link to the course syllabus.&lt;/p&gt;
&lt;h3&gt;
  
  
  \ Skills
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\section&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Skills&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For this section, I wanted to list all the tools/languages I know and have experience with, for which I added different &lt;code&gt;\subsections&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Programming:&lt;/strong&gt; all the languages I have experience with&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure:&lt;/strong&gt; all the infra tools I have experience with&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; all the storage solutions I have experience with&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiar With:&lt;/strong&gt; all the technologies I have used for personal projects or PoC at different companies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  \ Languages
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\section&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Languages&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The last section is dedicated to the languages you speak, with information about your level. In my case I have used the &lt;a href="https://europa.eu/europass/en/common-european-framework-reference" rel="noopener noreferrer"&gt;Common European Framework of Reference&lt;/a&gt;. E.g.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\subsection&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Languages&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\textbf&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Spanish:&lt;span class="p"&gt;}&lt;/span&gt; Native&lt;span class="k"&gt;\\&lt;/span&gt;
&lt;span class="k"&gt;\textbf&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;English:&lt;span class="p"&gt;}&lt;/span&gt; C1&lt;span class="k"&gt;\\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  # Work Experience
&lt;/h2&gt;

&lt;p&gt;This is, of course, the main section of our resume. Over the years I learned that a good work experience description should provide the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Company Name and Website&lt;/li&gt;
&lt;li&gt;Your Position&lt;/li&gt;
&lt;li&gt;Month and year&lt;/li&gt;
&lt;li&gt;Location&lt;/li&gt;
&lt;li&gt;Very small description of the company/product&lt;/li&gt;
&lt;li&gt;What where your main tasks/accomplishments in the company. (Here is the real deal so let's get into more details in the next section).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tech Stack&lt;/strong&gt;: the company's tech stack. Be careful about not revealing &lt;strong&gt;sensitive&lt;/strong&gt; information.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  # Describing your position at company X
&lt;/h2&gt;

&lt;p&gt;I have seen a lot of resumes during my career and I think most of the people fail at describing what they've done while working at company X. So to make it easier, let's list what a  good description should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Achievements&lt;/strong&gt;: what you have achieved while working in company X? Did you create something amazing? As example, you could write: "I refactored the search algorithm which led to an improvement of 70% in performance". It must be something you feel &lt;strong&gt;proud&lt;/strong&gt; about.&lt;/li&gt;
&lt;li&gt;What tasks did you &lt;strong&gt;enjoy&lt;/strong&gt; at this position? "I created a PoC to add &lt;code&gt;rust&lt;/code&gt; to our event processor"; "I created new tools for automatize deployments"&lt;/li&gt;
&lt;li&gt;Some &lt;strong&gt;trivial tasks&lt;/strong&gt;: "Prepared migration from Vue to React". Maybe you didn't enjoy these tasks but they were part of your tasks at company X.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E.g.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\runsubsection&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\href&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;https://cool-00.example/&lt;span class="p"&gt;}{&lt;/span&gt;Cool Company 00&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="k"&gt;\descript&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;| Senior Software Engineer&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\location&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\ello&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\textbf&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Aug 2021 – Present&lt;span class="p"&gt;}&lt;/span&gt; | Munich, Germany | Cool platform&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="k"&gt;\vspace&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\topsep&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;\begin{tightemize}&lt;/span&gt;
&lt;span class="k"&gt;\item&lt;/span&gt; Developed a cool algorithm that increased the speed of transactions by 70&lt;span class="k"&gt;\%&lt;/span&gt;
&lt;span class="k"&gt;\item&lt;/span&gt; Migrated cloud to AWS
&lt;span class="k"&gt;\item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\bf&lt;/span&gt; Tech Stack:&lt;span class="p"&gt;}&lt;/span&gt; NodeJS, ReactJS, Typescript, JWT, Web Sockets, Jest, ExpressJS, Go, RabbitMQ, Temporal, Kubernetes, PostgreSQL, GCloud, Github Actions, Argo CD, Sentry&lt;span class="k"&gt;\newline&lt;/span&gt;
&lt;span class="nt"&gt;\end{tightemize}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  # Fonts
&lt;/h2&gt;

&lt;p&gt;Ideally, you want to choose fonts that are easy to read and do not look too &lt;strong&gt;fancy&lt;/strong&gt;. For this project I have used 2 &lt;a href="https://fonts.google.com/" rel="noopener noreferrer"&gt;google fonts&lt;/a&gt; in order to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;have a &lt;strong&gt;geek&lt;/strong&gt; font for the Skills, locations, positions.&lt;/li&gt;
&lt;li&gt;have a "normal" font for the jobs descriptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can easily change these fonts in the &lt;code&gt;resume.cls&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\setmainfont&lt;/span&gt;&lt;span class="na"&gt;[Color=primary, Path = fonts/maven-pro/, BoldFont=MavenPro-SemiBold]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;MavenPro-Regular&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  # Colors
&lt;/h2&gt;

&lt;p&gt;Colors are defined as follow&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\definecolor&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;primary&lt;span class="p"&gt;}{&lt;/span&gt;HTML&lt;span class="p"&gt;}{&lt;/span&gt;2b2b2b&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="k"&gt;\definecolor&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;headings&lt;span class="p"&gt;}{&lt;/span&gt;HTML&lt;span class="p"&gt;}{&lt;/span&gt;6A6A6A&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\definecolor&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;subheadings&lt;span class="p"&gt;}{&lt;/span&gt;HTML&lt;span class="p"&gt;}{&lt;/span&gt;333333&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\definecolor&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;links&lt;span class="p"&gt;}{&lt;/span&gt;HTML&lt;span class="p"&gt;}{&lt;/span&gt;33AFFF&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Note that &lt;code&gt;links&lt;/code&gt; is our &lt;strong&gt;accent&lt;/strong&gt; color.&lt;/p&gt;

&lt;p&gt;That's it!&lt;br&gt;
This post ended up being longer than I expected. I hope you found something useful here and I'm looking forward to see your customizations!&lt;/p&gt;

&lt;p&gt;👽&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/protiumx" rel="noopener noreferrer"&gt;
        protiumx
      &lt;/a&gt; / &lt;a href="https://github.com/protiumx/dev.resume" rel="noopener noreferrer"&gt;
        dev.resume
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Your new pretty and minimalist resume
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;dev.resume&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;Your new pretty and minimalist resume.
Based on: &lt;a href="https://github.com/deedydas/Deedy-Resume" rel="noopener noreferrer"&gt;https://github.com/deedydas/Deedy-Resume&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Preview&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/protiumx/dev.resumepreview.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fprotiumx%2Fdev.resumepreview.png" alt="preview"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Fonts&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Main font: &lt;a href="https://fonts.google.com/specimen/Maven+Pro" rel="nofollow noopener noreferrer"&gt;MavenPro&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Skills font: &lt;a href="https://fonts.google.com/specimen/Source+Code+Pro" rel="nofollow noopener noreferrer"&gt;SourceCodePro&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can change the fonts as you want. I recommend taking a look at &lt;a href="https://fonts.google.com/" rel="nofollow noopener noreferrer"&gt;google fonts&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Colors&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;You can change any color in the &lt;a href="https://github.com/protiumx/dev.resumeresume.cls" rel="noopener noreferrer"&gt;class definition&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;primary: &lt;code&gt;#2b2b2b&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;headings: &lt;code&gt;#6A6A6A&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;subheadings: &lt;code&gt;#333333&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;links: &lt;code&gt;#33AFFF&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Commands&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;\namesection&lt;/code&gt;: The resume header for your name. It expect to arguments: first name and last name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\runsubsection&lt;/code&gt;: For company name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\location&lt;/code&gt;: Location of the company&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Compiling&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;You need a &lt;code&gt;xelatex&lt;/code&gt; compiler. I used &lt;a href="https://www.overleaf.com/" rel="nofollow noopener noreferrer"&gt;overleaf&lt;/a&gt; to edit and compile my resume.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Sponsorship&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;If you find this project useful you can support my work with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/p3kqm9Z2h" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8ca6cbc30815bc3ef458755d26604c424652dc18a51df3b3d708126667c31c91/68747470733a2f2f63646e2e6275796d6561636f666665652e636f6d2f627574746f6e732f76322f64656661756c742d7265642e706e67" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/protiumx/dev.resume" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>latex</category>
      <category>resume</category>
      <category>cv</category>
      <category>career</category>
    </item>
  </channel>
</rss>
