<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>danigm.net - gnome</title><link>https://danigm.net/</link><description></description><lastBuildDate>Fri, 05 Jun 2026 12:00:00 +0200</lastBuildDate><item><title>Take it easy. A guide to avoid burnown during the Vulnpocalypse</title><link>https://danigm.net/take-it-easy.html</link><description>&lt;p&gt;Do not let the AI to remove the fun part from software development.
We shouldn't allow gen AI to write software just because it "can".
First, we must ask if it "should" do it, and even then, we should ask
if we &lt;strong&gt;want&lt;/strong&gt; to delegate the fun part, the thinking, the writing,
the learning.&lt;/p&gt;
&lt;p&gt;Remember what's important, journey before destination, &lt;strong&gt;we are the
Code&lt;/strong&gt;:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/mb3uK-_QkOo" title="We are the art" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;Do not let AI to destroy the community, do not let it destroy the
&lt;a href="https://linguacelta.com/blog/2026/05/LLMs.html"&gt;technological knowledge commons&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;Open Source maintainers are dealing with a lot of new reports and
pressure to "fix" the project due to generative AI.&lt;/p&gt;
&lt;p&gt;We need to find a way of stopping this and get back to something
maintainable before all maintainers get burned out and look for a job
in a farm:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100% secure software doesn't exists, so there will be always a
   possible CVE there. As &lt;a href="https://en.wikiquote.org/wiki/Gene_Spafford"&gt;Spaf said in 1989&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The only truly secure system is one that is powered off, cast in a
block of concrete and sealed in a lead-lined room with armed guards
- and even then I have my doubts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Fixing bugs, adds new bugs, and if you need to fix something quick,
   the probability of new bugs will be higher. Do not forget about the
   First Law of Programming:&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;If it works, don't touch it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The amount of CVE reports is lowering the CVE credibility and
   quality, so if everything is a &lt;em&gt;"high"&lt;/em&gt; security issue, we can't
   prioritize now and these reports are not different from random
   issues in github. Do not listen to &lt;a href="https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf"&gt;The Boy Who Cried Wolf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Stable software is sable because it doesn't change too much. It's
   something that we are willing to loose trying to reach the
   impossible of 100% secure software?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The actual problem&lt;/h2&gt;
&lt;p&gt;There's a lot of money in AI tech right now, and everyone is trying to
make the best gen AI tool or just pretend that their tool is the best.&lt;/p&gt;
&lt;p&gt;In relation with the software analysis and writing, targeting the
open source is the obvious strategy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It's interesting to scrap every line of code, patch, pull request,
   issue and discussion around software to train your model, so AI
   scrappers are DDoSing open source projects infrastructure.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To promote their tools or themselves, &lt;em&gt;Security Researches&lt;/em&gt; are
   using AI to target any project, reporting &lt;em&gt;High security
   vulnerabilities&lt;/em&gt;, with the only goal of getting a CVE number to say
   how good they are.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This second point is affecting maintainers, because now you are
receiving a lot of poor quality security reports, that are generated
with AI and that looks plausible and are hard to read. You need to
spend a lot of time to check if there's an actual wolf there or if
it's again this boy that's tricking me.&lt;/p&gt;
&lt;p&gt;This is burning the energy of maintainers, that instead of doing
something productive are wasting their limited time talking with a
&lt;a href="https://en.wikipedia.org/wiki/Stochastic_parrot"&gt;Stocatic Parrot&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Do not let the AI Bros to use classic manipulation techniques on you!&lt;/h2&gt;
&lt;p&gt;A lot of open source projects are maintained by volunteers that do the
work with passion and love. And even if it's the job that paid your
bills, the maintainer can feel the &lt;a href="https://daniel.haxx.se/blog/2026/05/26/the-pressure/"&gt;pressure&lt;/a&gt;. When someone put a
lot of love in something and work on it during years, it's part of his
identity, so attacking the software is like attacking the person
behind it.&lt;/p&gt;
&lt;p&gt;This is nothing new, and a lot of people take advantage of this
emotional link to manipulate the maintainer to do something that he
do not want to do.&lt;/p&gt;
&lt;p&gt;AI bros are using these techniques, do not let them to manipulate you
and define your project agenda.&lt;/p&gt;
&lt;p&gt;Here's a (not complete) list of known manipulation techniques that you
can detect (and disarm!) in your daily community work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flooding the queue&lt;/strong&gt;. Just create so many new issues that the
   actual maintainers can't deal with it. You feel responsible for the
   project and feel bad because &lt;em&gt;your TO-DO list&lt;/em&gt; is growing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;This software is not secure (doesn't do what I want), I will use
   this other one instead that's better&lt;/strong&gt;. The classic, "GNOME doesn't
   allow me to change this specific preference, I'll use KDE from now
   on".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;This software is low quality, it doesn't follow the (my random)
   quality standards&lt;/strong&gt;. Direct attack to the maintainer self-esteem.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gaslighting software development&lt;/strong&gt;. LLM are expert at this and
   people that uses it just copy the tactic. When the maintainer
   detects something weird and just tries to blame the other person
   for reporting nonsense and wasting all people time, it starts to
   invent new arguments and ignore the previous interaction.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, take it easy, and remember the best clause in almost any software
project, &lt;strong&gt;THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
PROGRAM IS WITH YOU&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;Disclaimer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Warranty&lt;/span&gt;.

&lt;span class="nv"&gt;THERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;NO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WARRANTY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PROGRAM&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;EXTENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PERMITTED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BY&lt;/span&gt;
&lt;span class="nv"&gt;APPLICABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LAW&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;EXCEPT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WHEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OTHERWISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;STATED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WRITING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;COPYRIGHT&lt;/span&gt;
&lt;span class="nv"&gt;HOLDERS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AND&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OTHER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARTIES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PROVIDE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;“&lt;span class="nv"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IS&lt;/span&gt;”&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WITHOUT&lt;/span&gt;
&lt;span class="nv"&gt;WARRANTY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ANY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;KIND&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;EITHER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;EXPRESSED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IMPLIED&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;INCLUDING&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;NOT&lt;/span&gt;
&lt;span class="nv"&gt;LIMITED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TO&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IMPLIED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WARRANTIES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MERCHANTABILITY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FITNESS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARTICULAR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PURPOSE&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ENTIRE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;RISK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QUALITY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AND&lt;/span&gt;
&lt;span class="nv"&gt;PERFORMANCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;YOU&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SHOULD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PROVE&lt;/span&gt;
&lt;span class="nv"&gt;DEFECTIVE&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;YOU&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ASSUME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;COST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;NECESSARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SERVICING&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;REPAIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OR&lt;/span&gt;
&lt;span class="nv"&gt;CORRECTION&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Is the software more insecure in 2026?&lt;/h2&gt;
&lt;p&gt;No. Anyone old enough could remember how insecure old software was. Do
you remember windows 98? Do you remember the internet when everything
was http (without that little s at the end), when people use ftp
to logging into their server and modify the php code directly on
production?&lt;/p&gt;
&lt;p&gt;It's true that today we have more dependency on technology, but it's
also true that everything is more secure, we have more and better
cryptography, we have different levels of isolation, virtual
environments, containers, virtual machines...&lt;/p&gt;
&lt;p&gt;But we have the feeling that since AI can analyse all the software and
look for vulnerabilities, we are doomed, because any stupid kid can
hack my over engineered GNU/Linux machine!&lt;/p&gt;
&lt;p&gt;First, that's not true, you need to know about security to get
something useful from any AI tool. But even if it was true, what can
you do about it? We need to be practical and find a balance between
risk and usefulness, so do not &lt;strong&gt;overestimate the risk&lt;/strong&gt; just because
everyone is talking about it right now.&lt;/p&gt;
&lt;p&gt;But even then, the security paranoia is not good for anyone. Software
is inherently buggy, people write software and makes mistakes, so a
possible vulnerability appears. In theory, these bugs are fixed when
discovered, so it's always recommended to update to the latest
version, because almost all known bugs will be fixed.&lt;/p&gt;
&lt;p&gt;But it's also known that new versions comes with new functionality and
code, and that means new "unknown" bugs or different behavior. That's
a headache, so that's why the stable and Long Term Support are popular
distributions, because &lt;strong&gt;"if it works, don't touch it"&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Stable packages just get the fixes, not new features, but fixes are
also code changes, so there's always a possibility to break something,
even with a patch update.&lt;/p&gt;
&lt;p&gt;The stable software has a lot of value, do not let the AI security
paranoia destroy that, and convert everything in a rolling release
with the latest and greatest (and possibly broken) software. Sometimes
it's better to keep using something old, with &lt;strong&gt;known&lt;/strong&gt;
vulnerabilities that you can mitigate, than use the latest with
&lt;strong&gt;unknown&lt;/strong&gt; new vulnerabilities that you can't do anything about.&lt;/p&gt;
&lt;h2&gt;I will fight AI with AI&lt;/h2&gt;
&lt;p&gt;Please, do not do that. What I was trying to argue during this long
post is not a technical problem. The current burnout problem in open
source is a social problem, you can't fix it with a new layer of
probabilistic tokens.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Community reaction against AI&lt;/strong&gt;. The current industry push for
   the usage of AI everywhere is affecting a lot of people, and as a
   reaction a lot of people are directly fighting back. Using gen AI
   just sends the message that you do not care enough to do it
   yourself, and destroy the trust on the project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;It doesn't worth it&lt;/strong&gt;. Even if the AI works (that it doesn't) it
   doesn't worth it. Writing code is easier than reviewing, you learn
   and grow with every new line of code that you write, delegating
   the fun part and personal growth part to an AI will make you work
   more miserable and you will be a junior forever.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;It doesn't create community&lt;/strong&gt;. Think about it, it's hard to get
   someone involved in a software project, but who will want to read
   or improve the code produced by a gen AI? The only future
   collaborator will be another AI.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Take it easy&lt;/h2&gt;
&lt;p&gt;Just remember, you can always say no, there's no hurry, and there's no
need to work on something that you don't want just because other
people consider that important.&lt;/p&gt;
&lt;p&gt;Free Source is something done by people, for people. The software is
important, but the community around it is sometimes more important. We
use Free source not because it's technically better (that it is), but
because we trust who, how and why are writing it.&lt;/p&gt;
&lt;p&gt;Remember why are you doing this, do not remove the Fun part, continue
with the &lt;a href="https://es.wikipedia.org/wiki/Just_for_fun"&gt;&lt;strong&gt;Just for Fun&lt;/strong&gt;&lt;/a&gt; mood.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 05 Jun 2026 12:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2026-06-05:/take-it-easy.html</guid><category>blog</category><category>suse</category><category>opensuse</category><category>gnome</category><category>open-source</category><category>AI</category></item><item><title>GNOME Tour in openSUSE and welcome app</title><link>https://danigm.net/gnome-tour-opensuse.html</link><description>&lt;p&gt;As a follow up of the &lt;a href="https://danigm.net/hackweek24.html"&gt;Hackweek 24 project&lt;/a&gt;, I've continued working
on the gnome-tour fork for openSUSE with custom pages to replace the
welcome application for openSUSE distributions.&lt;/p&gt;
&lt;h2&gt;GNOME Tour modifications&lt;/h2&gt;
&lt;p&gt;All the modifications are on top of &lt;a href="https://gitlab.gnome.org/GNOME/gnome-tour/"&gt;upstream gnome-tour&lt;/a&gt; and
stored in the &lt;a href="https://github.com/openSUSE/gnome-tour"&gt;openSUSE/gnome-tour repo&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Custom &lt;strong&gt;initial page&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/gnome-tour-opensuse.png" /&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A new donations page&lt;/strong&gt;. In openSUSE we remove the popup from GNOME
   shell for donations, so it's fair to add it in this place.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/gnome-tour-donation.png" /&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Last page with custom openSUSE links&lt;/strong&gt;, this one is the used for
   opensuse-welcome app.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;opensuse-welcome package&lt;/h2&gt;
&lt;p&gt;The &lt;a href="github.com/openSUSE/openSUSE-welcome"&gt;original opensuse-welcome&lt;/a&gt; is a qt application, and this one
is used for all desktop environments, but it's more or less
unmaintained and looking for a replacement, we can use the gnome-tour
fork as the default welcome app for all desktop without a custom app.&lt;/p&gt;
&lt;p&gt;To do a minimal desktop agnostic opensuse-welcome application, I've
modified the gnome-tour to also generate a second binary but just with
the last page.&lt;/p&gt;
&lt;p&gt;The new opensuse-welcome rpm package is built as a subpackage of
&lt;a href="https://src.opensuse.org/pool/gnome-tour"&gt;gnome-tour&lt;/a&gt;. This new application is minimal and it doesn't have
lots of requirements, but as it's a gtk4 application, it requires gtk
and libadwaita, and also depends on gnome-tour-data to get the
resoures of the app.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/opensuse-welcome.png" /&gt;
&lt;/p&gt;

&lt;p&gt;To improve this welcome app we need to review the translations,
because I added three new pages to the gnome-tour and that specific
pages are not translated, so I should regenerate the .po files for all
languages and upload to &lt;a href="https://l10n.opensuse.org/"&gt;openSUSE Weblate&lt;/a&gt; for translations.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Tue, 21 Oct 2025 12:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2025-10-21:/gnome-tour-opensuse.html</guid><category>blog</category><category>gnome-tour</category><category>welcome</category><category>suse</category><category>gsoc</category><category>gnome</category></item><item><title>Log Detective: GSoC 2025 (part 2)</title><link>https://danigm.net/gsoc-2025-2.html</link><description>&lt;p&gt;This week is the last week of Google Summer of Code for this year
edition, and I'll do a summary of what we have been doing and future
plans for the Log Detective integration in openSUSE.&lt;/p&gt;
&lt;p&gt;I wrote a blog post about this project when it starts, so if you
didn't read, you can &lt;a href="https://danigm.net/gsoc-2025.html"&gt;take a look&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All the work and code was done in the
&lt;a href="https://github.com/openSUSE/logdetective-obs/"&gt;logdetective-obs github repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Aazam, aka the intern, also wrote
&lt;a href="https://alcadeus.hashnode.dev/tag/gsoc"&gt;some blog post about his work&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/gsoc.png" /&gt;
&lt;/p&gt;

&lt;h2&gt;Initial Research&lt;/h2&gt;
&lt;p&gt;The idea of the project was to explore ways of integration of
&lt;a href="https://log-detective.com"&gt;LogDetective&lt;/a&gt; with the openSUSE dev
workflow. So we started collecting some build failures in the openSUSE
build service and testing with log detective to check if the output is
smart or even relevant.&lt;/p&gt;
&lt;p&gt;The intern creates a script to collect log from build failures and we
store the LLM answer.&lt;/p&gt;
&lt;p&gt;Doing this we detected that the log-detective.com explain is very slow
and the &lt;a href="https://pypi.org/project/logdetective/"&gt;local tool&lt;/a&gt; is less
accurate.&lt;/p&gt;
&lt;p&gt;The results that we got weren't too good, but at this point of the
project is something expected. The model is being trained and will
improve with every new log that users send.&lt;/p&gt;
&lt;h2&gt;Local vs Remote, model comparison&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;logdetective&lt;/code&gt; command line tool is nice, but LLM requires a lot
of resources to run locally. This tool also uses the models published
by fedora in &lt;a href="https://huggingface.co/fedora-copr"&gt;huggingface.co&lt;/a&gt;, so
it's not as accurate as the remote instance that has a better trained
model and is up to date.&lt;/p&gt;
&lt;p&gt;In any case, the local &lt;code&gt;logdetective&lt;/code&gt; tool is interesting and, at this
moment, it's faster than the deployed log-detective.com.&lt;/p&gt;
&lt;p&gt;Using this tool, Aazam did some research, comparing the output with
&lt;a href="https://github.com/openSUSE/logdetective-obs/tree/main/model-testing"&gt;different models&lt;/a&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;logdetective apache-arrow_standard_x86_64.log\
  --model unsloth/Qwen2.5-Coder-7B-Instruct-128K-GGUF\
  -F Qwen2.5-Coder-7B-Instruct-Q4_K_M.gguf\
  --prompt ~/prompt.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So using other specific models, the logdetective tool can return
better results.&lt;/p&gt;
&lt;h2&gt;The plugin&lt;/h2&gt;
&lt;p&gt;openSUSE packagers uses the &lt;a href="https://github.com/openSUSE/osc"&gt;osc&lt;/a&gt;
tool, to build packages in build.opensuse.org. This tool can be
extended with plugins and we created a
&lt;a href="https://pypi.org/project/osc_ld_plugin/"&gt;new plugin&lt;/a&gt; to use
log-detective.&lt;/p&gt;
&lt;p&gt;So a packager can get some AI explanation about a build failure with a
simple command:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;osc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ld&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openSUSE_Tumbleweed&lt;/span&gt;
&lt;span class="err"&gt;🔍&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Running&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logdetective&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;openSUSE_Tumbleweed&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="p"&gt;)...&lt;/span&gt;
&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opensuse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;devel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openSUSE_Tumbleweed&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;_log&lt;/span&gt;
&lt;span class="err"&gt;🌐&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sending&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LogDetective&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RPM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`python-pygame`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;during&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;
&lt;span class="n n-Quoted"&gt;`%check`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;indicated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bad exit status from /var/tmp/rpm-&lt;/span&gt;
&lt;span class="s2"&gt;tmp.hiddzT (%check)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;occurred&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;due&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seven&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;six&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;skipped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;



&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;primary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;causing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seems&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`surface_test.py`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snippets&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;



&lt;span class="err"&gt;[[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;278s&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;278s&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/home/abuild/rpmbuild/BUILD/python-&lt;/span&gt;
&lt;span class="s2"&gt;pygame-2.6.1-build/BUILDROOT/usr/lib64/python3.11/site-&lt;/span&gt;
&lt;span class="s2"&gt;packages/pygame/tests/surface_test.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;284&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_copy_rle&lt;/span&gt;
&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;[[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;278s&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;278s&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/home/abuild/rpmbuild/BUILD/python-&lt;/span&gt;
&lt;span class="s2"&gt;pygame-2.6.1-build/BUILDROOT/usr/lib64/python3.11/site-&lt;/span&gt;
&lt;span class="s2"&gt;packages/pygame/tests/surface_test.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;274&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="n"&gt;test_mustlock_surf_alpha_rle&lt;/span&gt;
&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;[[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;278s&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;278s&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/home/abuild/rpmbuild/BUILD/python-&lt;/span&gt;
&lt;span class="s2"&gt;pygame-2.6.1-build/BUILDROOT/usr/lib64/python3.11/site-&lt;/span&gt;
&lt;span class="s2"&gt;packages/pygame/tests/surface_test.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;342&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_solarwolf_rle_usage&lt;/span&gt;
&lt;span class="err"&gt;]&lt;/span&gt;



&lt;span class="n"&gt;These&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;errors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;suggest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;there&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;
&lt;span class="n n-Quoted"&gt;`test_copy_rle`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`test_mustlock_surf_alpha_rle`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`test_solarwolf_rle_usage`&lt;/span&gt;
&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`surface_test.py`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;To&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;



&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Identify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cause&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;errors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;2.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;found&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;either&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modifying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correcting&lt;/span&gt;
&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;underlying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;problem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;they&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;3.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Re&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RPM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`surface_test.py`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;



&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s also recommended to review the other test failures and skipped steps, as&lt;/span&gt;
&lt;span class="s1"&gt;they might indicate other issues with the package or its dependencies that&lt;/span&gt;
&lt;span class="s1"&gt;should be addressed.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or if we are building locally, it's possible to run using the
installed &lt;code&gt;logdetective&lt;/code&gt; command line. But this method is less
accurate, because the model used is not smart enough, yet:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;osc&lt;span class="w"&gt; &lt;/span&gt;build
&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;osc&lt;span class="w"&gt; &lt;/span&gt;ld&lt;span class="w"&gt; &lt;/span&gt;--local-log&lt;span class="w"&gt; &lt;/span&gt;--repo&lt;span class="w"&gt; &lt;/span&gt;openSUSE_Tumbleweed
Found&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;log:&lt;span class="w"&gt; &lt;/span&gt;/var/tmp/build-root/openSUSE_Tumbleweed-x86_64/.build.log
🚀&lt;span class="w"&gt; &lt;/span&gt;Analyzing&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;log:&lt;span class="w"&gt; &lt;/span&gt;/tmp/tmpuu1sg1q0
INFO:logdetective:Loading&lt;span class="w"&gt; &lt;/span&gt;model&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;fedora-copr/Mistral-7B-Instruct-v0.3-GGUF
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Future plans&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Adding &lt;code&gt;submit-log&lt;/code&gt; functionality to &lt;code&gt;osc-ld-plugin&lt;/code&gt;.
   &lt;a href="https://github.com/openSUSE/logdetective-obs/pull/17/"&gt;In-progress&lt;/a&gt;.
   This will make it easier to collaborate with log-detective.com,
   allowing openSUSE packagers to send new data for model training.&lt;/li&gt;
&lt;li&gt;Gitea bot. openSUSE development workflow is moving to
   &lt;a href="https://src.opensuse.org/products/PackageHub"&gt;git&lt;/a&gt;, so we can
   create a bot that comment on Pull Request with packages that fails
   to build. Something similar to what fedora people have for
   &lt;a href="https://gitlab.com/redhat/centos-stream/rpms/gimp/-/merge_requests/9#note_2514178124"&gt;centos gitlab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Add a new tab to &lt;a href="https://log-detective.com"&gt;log-detective.com&lt;/a&gt; website to submit logs directly
   &lt;a href="https://github.com/fedora-copr/logdetective-website/issues/292"&gt;from OBS&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This was an interesting Summer of Code project. I learned a bit about
LLM. I think that this project has a lot of potential, this can be
integrated in different workflows and an expert LLM can be a great
tool to help packagers, summarizing big logs, tagging similar
failures, etc.&lt;/p&gt;
&lt;p&gt;We have a lot of packages and a lot of data in
&lt;a href="https://build.opensuse.org"&gt;OBS&lt;/a&gt;, so we should start to feed the LLM with
this data, and in combination with the fedora project, the
log-detective could be a real expert in open source RPM packaging.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Wed, 27 Aug 2025 07:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2025-08-27:/gsoc-2025-2.html</guid><category>blog</category><category>ai</category><category>rpm</category><category>suse</category><category>gsoc</category><category>gnome</category></item><item><title>Log Detective: Google Summer of Code 2025</title><link>https://danigm.net/gsoc-2025.html</link><description>&lt;p&gt;I'm glad to say that I'll participate again in the &lt;a href="https://summerofcode.withgoogle.com/"&gt;GSoC&lt;/a&gt;, as
mentor. This year we will try to improve the RPM packaging workflow
using AI, as part of the &lt;a href="https://www.opensuse.org/"&gt;openSUSE&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;So this summer I'll be mentoring an intern that will research how to
integrate &lt;a href="https://log-detective.com/"&gt;Log Detective&lt;/a&gt; with openSUSE tooling to improve the
packager workflow to maintain rpm packages.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/gsoc.png" /&gt;
&lt;/p&gt;

&lt;h2&gt;Log Detective&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://log-detective.com/"&gt;Log Detective&lt;/a&gt; is an initiative created by the &lt;a href="https://fedoraproject.org/"&gt;Fedora project&lt;/a&gt;,
with the goal of&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Train an AI model to understand RPM build logs and explain the
failure in simple words, with recommendations how to fix it. You
won't need to open the logs at all."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/log-detective.png" /&gt;
&lt;/p&gt;

&lt;p&gt;As a project that was promoted by Fedora, it's highly integrated with
the build tools around this distribution and RPM packages. But RPM
packages are used in a lot of different distributions, so this
"expert" LLM will be helpful for everyone doing RPM, and everyone
doing RPM, &lt;em&gt;should&lt;/em&gt; contribute to it.&lt;/p&gt;
&lt;p&gt;This is open source, so if, at openSUSE, we want to have something
similar to improve the &lt;a href="https://build.opensuse.org/"&gt;OBS&lt;/a&gt;, we don't need to reimplement it, we
can collaborate. And that's &lt;a href="https://github.com/fedora-copr/logdetective-website/issues/213"&gt;the idea&lt;/a&gt; of this GSoC project.&lt;/p&gt;
&lt;p&gt;We want to use Log Detective, but also collaborate with failures from
openSUSE to improve the training and the AI, and this should benefit
openSUSE but also will benefit Fedora and all other RPM based
distributions.&lt;/p&gt;
&lt;h2&gt;The intern&lt;/h2&gt;
&lt;p&gt;The selected intern is &lt;a href="https://summerofcode.withgoogle.com/programs/2025/projects/SZ1Va8yZ"&gt;Aazam Thakur&lt;/a&gt;. He studies at University of
Mumbai, India. He has experience in using SUSE as he has previously
worked on SLES 15.6 during his previous &lt;a href="https://openmainframeproject.org/blog/summer-mentorship-2024-automating-feilong-packaging-a-technical-odyssey/"&gt;summer mentorship&lt;/a&gt; at
OpenMainFrame Project for RPM packaging.&lt;/p&gt;
&lt;p&gt;I'm sure that he will be able to achieve great things during these
three months. The project looks very promising and it's one of the
things where AI and LLM will shine, because digging into logs is
always something difficult and if we train a LLM with a lot of data it
can be really useful to categorize failures and give a short
description of what's happening.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Mon, 09 Jun 2025 12:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2025-06-09:/gsoc-2025.html</guid><category>blog</category><category>ai</category><category>rpm</category><category>suse</category><category>gsoc</category><category>gnome</category></item><item><title>Python 2</title><link>https://danigm.net/python2.html</link><description>&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/python2.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;In 2020, the
&lt;a href="https://www.python.org/doc/sunset-python-2/"&gt;Python foundation declared Python 2 as not maintained anymore&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Python 2 is really old, not maintained and should not be used by
anyone in any modern environment, but software is complex and python2
still exists in some modern Linux distributions like Tumbleweed.&lt;/p&gt;
&lt;p&gt;The past week the &lt;a href="https://build.opensuse.org/request/show/1240106"&gt;request to delete Python 2&lt;/a&gt; from Tumbleweed was
created and is going through the staging process.&lt;/p&gt;
&lt;p&gt;The main package keeping Python 2 around for Tumbleweed was Gimp 2,
that doesn't depends directly on Python 2, but some of the plugins
depends on it. Now that we've Gimp 3 in Tumbleweed, we are able to
finally remove it.&lt;/p&gt;
&lt;h2&gt;Python 2&lt;/h2&gt;
&lt;p&gt;The first version of Python 2 was released around 2000, so it's now 25
years old. That's not true, because software is a living creature, so
as you may know, Python 2 grew during the following years with patch
and minor releases until 2020 that was the final release 2.7.18.&lt;/p&gt;
&lt;p&gt;But even when it was maintained until 2020, it was deprecated for a
long time so everyone "should" have time to migrate to python 3.&lt;/p&gt;
&lt;h2&gt;Py3K&lt;/h2&gt;
&lt;p&gt;I started to write python code around the year 2006. I was bored
during a summer internship at my third year of computer science, and I
decided to learn something new. In the following months / years I
heard a lot about the futurist &lt;a href="https://peps.python.org/pep-3000/"&gt;Python 3000&lt;/a&gt;, but I didn't worry too
much until it was officially released and the &lt;a href="https://docs.python.org/3.10/library/2to3.html"&gt;migration&lt;/a&gt; started to be
a thing.&lt;/p&gt;
&lt;p&gt;If you have ever write python2 code you will know about some of the
main differences with python3:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;print vs print()&lt;/li&gt;
&lt;li&gt;raw_input() vs input()&lt;/li&gt;
&lt;li&gt;unicode() vs str&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some tools appeared to make it easier to migrate from python2 to
python3, and even it was possible to have code compatible with both
versions at the same time using the &lt;code&gt;__future__&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;You should have heard about the &lt;a href="https://pypi.org/project/six/"&gt;six&lt;/a&gt; package, 2 * 3 = 6. Maybe the
name should be five instead of six, because it was a Python "2 and 3"
compatibility library.&lt;/p&gt;
&lt;h2&gt;Python in Linux command line&lt;/h2&gt;
&lt;p&gt;When python3 started to be the main python, there were some discussion
about how to handle that in different Linux distributions. The
/usr/bin/python binary was present and everyone expect that to be
python2, so almost everyone decided to keep that relation forever and
distribute python3 as /usr/bin/python3, so you can have both installed
without conflicts and there's no confusion.&lt;/p&gt;
&lt;p&gt;But python is an interpreted language, and if you have python code,
you can't tell if it's python2 or python3. The shebang line in the
executable python scripts should point to the correct interpreter and
that should be enough like &lt;code&gt;#!/usr/bin/python3&lt;/code&gt; will use the python3
interpreter and &lt;code&gt;#!/usr/bin/python&lt;/code&gt; will use python2.&lt;/p&gt;
&lt;p&gt;But this is not always true, some distributions uses python3 in
&lt;code&gt;/usr/bin/python&lt;/code&gt; like Archlinux or if you create a virtualenv with
python3, the &lt;code&gt;python&lt;/code&gt; binary points to the python3 interpreter, so a
shebang like &lt;code&gt;#!/usr/bin/python&lt;/code&gt; could be something valid for a
python3 script.&lt;/p&gt;
&lt;p&gt;In any case, the recommended and safest way is to always use &lt;code&gt;python3&lt;/code&gt;
binary because that way it'll work correctly "everywhere".&lt;/p&gt;
&lt;h2&gt;Goodbye&lt;/h2&gt;
&lt;p&gt;It's time to say goodbye to &lt;code&gt;python2&lt;/code&gt;, at least we can remove it now
from Tumbleweed. It'll be around for some more time in Leap, but it's
the time to let it go.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Mon, 27 Jan 2025 12:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2025-01-27:/python2.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>opensuse</category></item><item><title>Hackweek 24</title><link>https://danigm.net/hackweek24.html</link><description>&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/hackweek24.png" width="50%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;It's the time for a new &lt;a href="https://hackweek.opensuse.org/"&gt;Hack Week&lt;/a&gt;. The Hack Week 24 was from
November 18th to November 22th, and I've decided to join the &lt;a href="https://hackweek.opensuse.org/projects/opensuse-welcome"&gt;New openSUSE-welcome&lt;/a&gt;
project this time.&lt;/p&gt;
&lt;p&gt;The idea of this project is to revisit the existing openSUSE welcome
app, and I've been trying to help here, specifically for the GNOME
desktop installation.&lt;/p&gt;
&lt;h2&gt;openSUSE-welcome&lt;/h2&gt;
&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/opensuse-welcome.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;Right now after installing any openSUSE distribution with a graphical
desktop, the user is welcomed on first login with a &lt;a href="https://github.com/openSUSE/openSUSE-welcome/"&gt;custom welcome app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This custom application is a Qt/QML with some basic information and
useful links.&lt;/p&gt;
&lt;p&gt;The same generic application is used for all desktops, and for popular
desktops right now exists upstream applications for this purpose, so
we were talking on Monday morning about it and decided to use specific
apps for desktops.&lt;/p&gt;
&lt;p&gt;So for GNOME, we can use the &lt;a href="https://gitlab.gnome.org/GNOME/gnome-tour/"&gt;GNOME Tour&lt;/a&gt; application.&lt;/p&gt;
&lt;h2&gt;gnome-tour&lt;/h2&gt;
&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/gnome-tour.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;GNOME Tour is a simple rust/gtk4 application with some fancy images in
a slideshow.&lt;/p&gt;
&lt;p&gt;This application is generic and just shows information about GNOME
desktop, so I created a &lt;a href="https://github.com/openSUSE/gnome-tour"&gt;fork for openSUSE&lt;/a&gt; to do some openSUSE
specific customization and use this application as openSUSE welcome in
GNOME desktop for Tumbleweed and Leap.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/gnome-tour-opensuse.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Desktop patterns, the welcome workflow&lt;/h2&gt;
&lt;p&gt;After some testing and investigation about the current workflow for
the welcome app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://code.opensuse.org/package/patterns-base/blob/master/f/patterns-base.spec#_861"&gt;x11_enhanced&lt;/a&gt; pattern recommends opensuse-welcome app.&lt;/li&gt;
&lt;li&gt;We can add a &lt;code&gt;Recommends: gnome-tour&lt;/code&gt; to the &lt;a href="https://code.opensuse.org/package/patterns-gnome/blob/master/f/patterns-gnome.spec#_240"&gt;gnome pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The application run using &lt;a href="https://specifications.freedesktop.org/autostart-spec/latest/"&gt;xdg autostart&lt;/a&gt;, so gnome-tour package
   should put the file in &lt;code&gt;/etc/xdg/autostart&lt;/code&gt; and set to hidden on
   close.&lt;/li&gt;
&lt;li&gt;In the case of having a system with multiple desktops, we can
   choose the specific welcome app using the &lt;code&gt;OnlyShowIn/NotShowIn&lt;/code&gt;
   &lt;a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/recognized-keys.html"&gt;config in desktop file&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So I've created a &lt;a href="https://github.com/openSUSE/openSUSE-welcome/pull/43"&gt;draft PR&lt;/a&gt; to do not show the openSUSE-welcome
app in GNOME, and I've also the &lt;a href="https://build.opensuse.org/package/show/home:dgarcia:branches:GNOME:Next/gnome-tour"&gt;gnome-tour fork&lt;/a&gt; in my home OBS
project.&lt;/p&gt;
&lt;p&gt;I've been testing this configuration in Tumbleweed with GNOME, KDE and
XFCE installed and it works as expected. The openSUSE-welcome is shown
in KDE and XFCE and the gnome-tour app is only shown in GNOME.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/gnome-tour-tumbleweed.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;The next steps to have the GNOME Tour app as default welcome for
openSUSE GNOME installation are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Send forked &lt;code&gt;gnome-tour&lt;/code&gt; package to &lt;code&gt;GNOME:Next&lt;/code&gt; project in OBS.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;Recommends: gnome-tour&lt;/code&gt; to &lt;code&gt;patterns-gnome&lt;/code&gt; to &lt;code&gt;GNOME:Next&lt;/code&gt; project in OBS.&lt;/li&gt;
&lt;li&gt;Make sure that any other welcome application is &lt;a href="https://github.com/openSUSE/openSUSE-welcome/pull/43"&gt;not shown in GNOME&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Review openQA tests that expect opensuse-welcome and adapt for the
   new application.&lt;/li&gt;
&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 22 Nov 2024 12:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2024-11-22:/hackweek24.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>opensuse</category><category>hackweek</category></item><item><title>rpmlint: Google Summer of Code 2024</title><link>https://danigm.net/gsoc-2024.html</link><description>&lt;p&gt;I'm glad to say that I'll participate again in the &lt;a href="https://summerofcode.withgoogle.com/"&gt;GSoC&lt;/a&gt;, as
mentor. This year we will continue the work done during the past year,
as part of the &lt;a href="https://www.opensuse.org/"&gt;openSUSE&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;So this summer I'll be mentoring an intern and we'll continue working
on improving the testing framework of the &lt;a href="https://github.com/openSUSE/mentoring/issues/204"&gt;rpmlint project&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/gsoc.png" /&gt;
&lt;/p&gt;

&lt;p&gt;This year we've a better testing framework, thanks to the work done
during the past Summer of Code, by Afrid. So the goal for this year is
to try to modernize existing tests and remove as much files as
possible from &lt;code&gt;test/binary&lt;/code&gt; directory, replacing those with mock
packages defined with python code.&lt;/p&gt;
&lt;p&gt;The selected intern is &lt;a href="https://blog-gsoc-2024.blogspot.com/2024/05/gsoc-improve-test-coverage-in-rpmlint.html"&gt;Luz Marina Montilla Marín&lt;/a&gt;. She has done
some initial work in the rpmlint project, creating the mock packages
for some tests and we've just started with the work to do during the
GSoC program, evaluating the tests that we've right now and planning
were to start.&lt;/p&gt;
&lt;p&gt;She studies at Córdoba, Spain, my hometown. Every year I try to reach
young people at different local universities, here in Andalucía, and
sometimes I'm able to convince some students to participate, like the
&lt;a href="https://danigm.net/gsoc-2020.html"&gt;GSoC 2020&lt;/a&gt;, when Alejandro Dominguez, from Seville, were working
on Fractal. So I'm happy that I'm increasing the number of free
software developers in my local community :D&lt;/p&gt;
&lt;p&gt;I'm sure that she will be able to achieve great things during these
three months, so I'm looking forward to start to code and see how far
can we go.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Thu, 30 May 2024 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2024-05-30:/gsoc-2024.html</guid><category>blog</category><category>gnome</category><category>software</category><category>rpmlint</category><category>suse</category><category>gsoc</category></item><item><title>Python 3.13 Beta 1</title><link>https://danigm.net/python313-beta1.html</link><description>&lt;p&gt;&lt;img src="/pictures/python-logo-master-v313-TM.png" width="100%" /&gt;&lt;/p&gt;
&lt;p&gt;Python &lt;a href="https://www.python.org/downloads/release/python-3130b1/"&gt;3.13 beta 1 is out&lt;/a&gt;, and I've been working on the openSUSE
Tumbleweed package to get it ready for the release.&lt;/p&gt;
&lt;h2&gt;Installing python 3.13 beta 1 in Tumbleweed&lt;/h2&gt;
&lt;p&gt;If you are adventurous enough to want to test the python 3.13 and you
are using openSUSE Tumbleweed, you can give it a try and install the
current devel package:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# zypper addrepo -p 1000 https://download.opensuse.org/repositories/devel:languages:python:Factory/openSUSE_Tumbleweed/devel:languages:python:Factory.repo&lt;/span&gt;
&lt;span class="c1"&gt;# zypper refresh&lt;/span&gt;
&lt;span class="c1"&gt;# zypper install python313&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;What's new in Python 3.13&lt;/h2&gt;
&lt;p&gt;Python interpreter is pretty stable nowadays and it doesn't change too
much to keep code compatible between versions, so if you are writing
modern Python, your code should continue working whit this new
version. But it's actively developed and new versions have cool new
functionalities.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3.13/whatsnew/3.13.html#a-better-interactive-interpreter"&gt;New and improved interactive interpreter&lt;/a&gt;, colorized prompts,
   multiline editing with history preservation, interactive help with
   &lt;code&gt;F1&lt;/code&gt;, history browsing with &lt;code&gt;F2&lt;/code&gt;, paste mode with &lt;code&gt;F3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A set of performance improvements.&lt;/li&gt;
&lt;li&gt;Removal of many deprecated modules: aifc, audioop, chunk, cgi,
   cgitb, crypt, imghdr, mailcap, msilib, nis, nntplib, ossaudiodev,
   pipes, sndhdr, spwd, sunau, telnetlib, uu, xdrlib, lib2to3.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Enabling Experimental JIT Compiler&lt;/h2&gt;
&lt;p&gt;The python 3.13 version will arrive with an &lt;a href="https://docs.python.org/3.13/whatsnew/3.13.html#experimental-jit-compiler"&gt;experimental functionality&lt;/a&gt;
to improve performance. We're building with the
&lt;code&gt;--enable-experimental-jit=yes-off&lt;/code&gt; so it's disabled by default but it
can be enabled with a virtualenv before launching:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_JIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3.13
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Free-threaded CPython&lt;/h2&gt;
&lt;p&gt;The python 3.13 has another build option to disable the Global
Interpreter Lock (&lt;code&gt;--disable-gil&lt;/code&gt;), but we're not enabling it because
in this case it's not possible to keep the same behavior. Building
with &lt;code&gt;disabled-gil&lt;/code&gt; will break compatibility.&lt;/p&gt;
&lt;p&gt;In any case, maybe it's interesting to be able to provide another
version of the interpreter with the GIL disabled, for specific cases
where the performance is something critical, but that's something to
evaluate.&lt;/p&gt;
&lt;p&gt;We can think about having a &lt;code&gt;python313-nogil&lt;/code&gt; package, but it's not
something trivial to be able to have &lt;code&gt;python313&lt;/code&gt; and &lt;code&gt;python313-nogil&lt;/code&gt;
at the same time in the same system installation, so I'm not planning
to work on that for now.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Wed, 22 May 2024 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2024-05-22:/python313-beta1.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>linux</category><category>python</category></item><item><title>Where's my python code?</title><link>https://danigm.net/wheres-my-python-code.html</link><description>&lt;p&gt;&lt;img src="/pictures/python-logo-master-v3-TM.png" width="100%" /&gt;&lt;/p&gt;
&lt;p&gt;Python is a interpreted language, so the python code are just text
files with the &lt;code&gt;.py&lt;/code&gt; extension. For simple scripts it's really easy to
have your files located, but when you starts to use dependencies and
different projects with different requirements the thing starts to get
more complex.&lt;/p&gt;
&lt;h2&gt;PYTHONPATH&lt;/h2&gt;
&lt;p&gt;The Python interpreter uses a list of paths to try to locate python
modules, for example this is what you can get in a modern GNU/Linux
distribution by default:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="mf"&gt;3.11.7&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="mi"&gt;2023&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="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GCC&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;linux&lt;/span&gt;
&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;copyright&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;credits&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;license&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python311.zip&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11/lib-dynload&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11/site-packages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11/_import_failed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib/python3.11/site-packages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are the default paths where the python modules are installed. If
you install any python module using your linux packaging tool, the
python code will be placed inside the &lt;code&gt;site-packages&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;So system installed python modules can be located in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/usr/lib/python3.11/site-packages&lt;/code&gt; for modules that are
   architecture independent (pure python, all &lt;code&gt;.py&lt;/code&gt; files)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usr/lib64/python3.11/site-packages&lt;/code&gt; for modules that depends on
   the arquitecture, that's something that uses low level libraries
   and needs to build so there are some &lt;code&gt;.so&lt;/code&gt; files.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;pip&lt;/h2&gt;
&lt;p&gt;When you need a new python dependency you can try to install from your
GNU/Linux distribution using the default package manager like
&lt;code&gt;zypper&lt;/code&gt;, &lt;code&gt;dnf&lt;/code&gt; or &lt;code&gt;apt&lt;/code&gt;, and those python files will be placed in the
system paths that you can see above.&lt;/p&gt;
&lt;p&gt;But distributions doesn't pack all the python modules and even if they
do, you can require an specific version that's different from the one
packaged in your favourite distribution, so in python it's common to
install dependencies from the &lt;a href="https://pypi.org/"&gt;Python Package Index (PyPI)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Python has a tool to install and manage Python packages that looks for
desired python modules in PyPI.&lt;/p&gt;
&lt;p&gt;You can install new dependencies with &lt;code&gt;pip&lt;/code&gt; just like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;django
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that command looks for the &lt;code&gt;django&lt;/code&gt; python module in the PyPI,
downloads and install it, in your user
&lt;code&gt;$HOME/.local/lib/python3.11/site-packages&lt;/code&gt; folder if you
use &lt;code&gt;--user&lt;/code&gt;, or in a global system path like &lt;code&gt;/usr/local/lib&lt;/code&gt; or
&lt;code&gt;/usr/lib&lt;/code&gt; if you run pip as root.&lt;/p&gt;
&lt;p&gt;But the usage of &lt;code&gt;pip&lt;/code&gt; directly in the system is something &lt;strong&gt;not
recommended today&lt;/strong&gt;, and even &lt;a href="https://packaging.python.org/en/latest/specifications/externally-managed-environments/#externally-managed-environments"&gt;it's disabled&lt;/a&gt; in some
distributions, like openSUSE Tumbleweed.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;danigm&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;django&lt;/span&gt;
&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;externally&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;managed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;

&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;externally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;managed&lt;/span&gt;
&lt;span class="err"&gt;╰─&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;To&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;wide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;zypper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python311&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;xyz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xyz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;trying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rpm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packaged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;virtual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python3&lt;/span&gt;&lt;span class="m m-Double"&gt;.11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rpm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packaged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;easiest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nx"&gt;pipx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xyz&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;virtual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pipx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;via&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nx"&gt;zypper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python311&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;pipx&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;believe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mistake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;distribution&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;breaking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;passing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;See&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PEP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;668&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;detailed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;specification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;virtualenvs&lt;/h2&gt;
&lt;p&gt;Following the current recommendation, the correct way of installing
third party python modules is to use &lt;code&gt;virtualenvs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;virtualenvs&lt;/code&gt; are just specific folders where you install your
python modules and some scripts that make's easy to use it in
combination with your system libraries so you don't need to modify the
&lt;code&gt;PYTHONPATH&lt;/code&gt; manually.&lt;/p&gt;
&lt;p&gt;So if you've a custom project and want to install python modules you
can create your own virtualenv and use pip to install dependencies
there:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm@localhost tmp&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm@localhost tmp&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm@localhost tmp&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;
&lt;span class="n"&gt;Collecting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Successfully&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;asgiref&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;3.7.2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;5.0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.4.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So all dependencies are installed in my new virtualenv folder and if I
use the python from the virtualenv it's using those paths, so all the
modules installed there are usable inside that virtualenv:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm&lt;/span&gt;&lt;span class="nd"&gt;@localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.11&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;contrib&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;shortcuts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;templatetags&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;views&lt;/span&gt;
&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;__pycache__&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm&lt;/span&gt;&lt;span class="nd"&gt;@localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import django; print(django.__version__)&amp;quot;&lt;/span&gt;
&lt;span class="mf"&gt;5.0.1&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm&lt;/span&gt;&lt;span class="nd"&gt;@localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deactivate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With virtualenvs you can have multiple python projects, with different
dependencies, isolated, so you use different dependencies when you
activate your desired virtualenv:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;activate &lt;code&gt;$ . ./myenv/bin/activate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;deactivate &lt;code&gt;$ deactivate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;High level tools to handle virtualenvs&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://docs.python.org/3/library/venv.html#module-venv"&gt;venv&lt;/a&gt; module is a default Python module and as you can see
above, it's really simple to use, but there are some tools that
provides some tooling around it, to make it easy for you, so usually
you don't need to use &lt;code&gt;venv&lt;/code&gt; directly.&lt;/p&gt;
&lt;h3&gt;pipx&lt;/h3&gt;
&lt;p&gt;For final python tools, that you are not going to use as dependencies
in your python code, the recommended tool to use is &lt;a href="https://github.com/pypa/pipx"&gt;pipx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/pictures/pipx_demo.gif" width="100%" /&gt;&lt;/p&gt;
&lt;p&gt;The tool creates virtualenv automatically and links the binaries so
you don't need to worry about anything, just use as a way to install
third party python applications and update/uninstall using it. The
&lt;code&gt;pipx&lt;/code&gt; won't mess your system libraries and each installation will use
a different virtualenv, so even tools with incompatible dependencies
will work nicely together in the same system.&lt;/p&gt;
&lt;h3&gt;Libraries, for Python developers&lt;/h3&gt;
&lt;p&gt;In the case of Python developers, when you need to manage dependencies
for your project, there are a lot of nice high level tools for
&lt;a href="https://packaging.python.org/en/latest/tutorials/managing-dependencies/#other-tools-for-application-dependency-management"&gt;managing dependencies&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pdm-project/pdm"&gt;PDM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pypa/hatch"&gt;hatch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/thoth-station/micropipenv"&gt;micropipenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jazzband/pip-tools"&gt;pip-tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://packaging.python.org/en/latest/key_projects/#pipenv"&gt;pipenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/python-poetry/poetry"&gt;poetry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These tools provides different ways of managing dependencies, but all
of them relies in the use of &lt;code&gt;venv&lt;/code&gt;, creating the virtualenv in
different locations and providing tools to enable/disable and manage
dependencies inside those virtualenvs.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;poetry&lt;/code&gt; creates virtualenvs by default inside the
&lt;code&gt;.cache&lt;/code&gt; folder, in my case I can find all poetry created virtualenvs
in:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/home/danigm/.cache/pypoetry/virtualenvs/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of these tools add other utilities on top of the dependency
management. Just for installing python modules easily you can always
use default &lt;code&gt;venv&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; modules, but for more complex projects
it's worth to investigate high level tools, because it'll make easy to
manage your project dependencies and virtualenvs.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There are a lot of python code inside any modern Linux distribution
and if you're a python developer it's possible to have a lot of python
code. Make sure to know the source of your modules and do not mix
different environments to avoid future headaches.&lt;/p&gt;
&lt;p&gt;As a final trick, if you don't know where's the actual code of some
python module in your running python script, you can always ask:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/tmp/myenv/lib64/python3.11/site-packages/django/__init__.py&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This could be even more complicated if you start to use containers
and different python versions, so keep you dependencies clean and up
to date and make sue that you know &lt;strong&gt;where is your Python code&lt;/strong&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 03 Feb 2024 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2024-02-03:/wheres-my-python-code.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>linux</category><category>python</category></item><item><title>Hackweek 23</title><link>https://danigm.net/hackweek23.html</link><description>&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/hackweek23.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://hackweek.opensuse.org/"&gt;Hack Week&lt;/a&gt; is the time SUSE employees experiment, innovate &amp;amp; learn
interruption-free for a whole week! Across teams or alone, but always without
limits.&lt;/p&gt;
&lt;p&gt;The Hack Week 23 was from November 6th to November 10th, and my project was to
gvie some &lt;a href="https://hackweek.opensuse.org/23/projects/gnome-love"&gt;love to the GNOME Project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before the start of the Hack week I asked in the GNOME devs Matrix channel,
what project needs some help and they gave me some ideas. At the end I decided
to work on the &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/"&gt;GNOME Calendar&lt;/a&gt;, more specifically, improving the test suite
and fixing &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1093"&gt;issues related to timezones&lt;/a&gt;, DST, etc.&lt;/p&gt;
&lt;h2&gt;GNOME Calendar&lt;/h2&gt;
&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/gnome-calendar.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;GNOME Calendar is a &lt;a href="https://gtk.org/"&gt;Gtk4 application&lt;/a&gt;, written in C, that heavily uses the
&lt;a href="https://gitlab.gnome.org/GNOME/evolution-data-server"&gt;evolution-data-server&lt;/a&gt; library. It's a desktop calendar application with a
modern user interface that can connect handle local and remote calendars. It's
integrated in the GNOME desktop.&lt;/p&gt;
&lt;p&gt;The current gnome-calendar project has some unit tests, using the &lt;a href="https://docs.gtk.org/glib/testing.html"&gt;GLib testing
framework&lt;/a&gt;. But right now there are just a few tests, so the main goal right
now is to increase the number of tests as much as possible, to detect new
problems and regressions.&lt;/p&gt;
&lt;p&gt;Testing a desktop application is not something easy to do. The unit tests can
check basic operations, structures and methods, but the user interaction and
how it's represented is something hard to test. So the best approach is to try
replicate user interactions and check the outputs.&lt;/p&gt;
&lt;p&gt;A more sophisticated approach could be to start to use the accessibility stack
in tests, so it's possible to verify the UI widgets output without the need of
rendering the app.&lt;/p&gt;
&lt;p&gt;With gnome-calendar there's another point of complexity for tests because it
relies on the evolution-data-server to be running, the app communicates with it
using dbus, so to be able to do more complex tests we should mock the
evolution-data-server and we should create fake data for testing.&lt;/p&gt;
&lt;h2&gt;My contribution&lt;/h2&gt;
&lt;p&gt;By the end of the week I've created four &lt;a href="https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests?scope=all&amp;amp;state=all&amp;amp;author_username=danigm&amp;amp;label_name%5B%5D=Timezones"&gt;Merge requests&lt;/a&gt;, three of them
have been merged now, and I'll continue working on this project in the
following weeks/months.&lt;/p&gt;
&lt;p&gt;I'm happy with the work that I was able to do during this Hack Week. I've
learned a bit about testing with GLib in C, and a lot about the
evolution-data-server, timezones and calendar problems.&lt;/p&gt;
&lt;p&gt;It's just a desktop calendar application, how hard it could be? Have you ever
deal with dates, times and different regions and time zones? It's a nightmare.
There are a lot of edge cases working with dates that can cause problems,
operations with dates in different time zones, changes in dates for daylight
saving, if I've an event created for October 29th 2023 at 2:30 it will happens
two times?&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/gnome-calendar-issues.png" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;A lot of problems could happen and there are a lot of bugs reported for
gnome-calendar related to this kind of issues, so working on this is not
something simple, it requires a lot of edge case testing and that's the plan,
to cover most of them with automated tests, because any small change could lead
to a bug related to time zones that won't be noticed until someone has an
appointment at a very specific time.&lt;/p&gt;
&lt;p&gt;And this week was very productive thanks to the people working on
gnome-calendar. Georges Stavracas reviews my MR very quickly and it was
possible to merge during the week, and Jeff Fortin does a great work with the
issues in gitlab and leading me to most relevant bugs.&lt;/p&gt;
&lt;p&gt;So after a week of going deep into the gnome-calendar source code it could be a
pity to just forget about it, so I'll try to keep the momentum and continue
working on this project, of course, I'll have just a few hours per week, but
any contribution is better than nothing. And maybe for the next summer I can
propose a Google Summer of Code project to get an intern working on this full
time.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 10 Nov 2023 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2023-11-10:/hackweek23.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>opensuse</category><category>hackweek</category></item><item><title>Updating GNOME shell extensions to GNOME 45</title><link>https://danigm.net/gnome-45-extensions.html</link><description>&lt;p&gt;&lt;img src="/pictures/GNOME45.webp" width="100%" /&gt;&lt;/p&gt;
&lt;p&gt;The new version of the &lt;a href="https://www.gnome.org/"&gt;GNOME desktop&lt;/a&gt; was released &lt;a href="https://foundation.gnome.org/2023/09/20/introducing-gnome-45/"&gt;more than one month&lt;/a&gt;
ago. It takes some time to arrive to the final user, because distributions
should integrate, tests and release the new desktop, and that's not something
simple, and it should integrate in the distribution release planning.&lt;/p&gt;
&lt;p&gt;So right now, it's possible that there's just a few people with the latest
version of the desktop right now, just people with rolling release distros or
people using testing versions of mayor distributions.&lt;/p&gt;
&lt;p&gt;This is one of the reasons because a lot of gnome-shell extensions aren't
updated to work with the latest version of GNOME, even after a few months,
because even developers doesn't have the latest version of the desktop and
it's not something "easy" to install, without a virtual machine or something
like that. Even if the update is just a change in the metadata.json, there
should be someone to test the extension, and even someone to request this
update, and that will happen once the mayor distributions release a new
version.&lt;/p&gt;
&lt;p&gt;I'm using Tumbleweed, that's a rolling release and GNOME 45 is here just after
the official release, but of course, a lot of extensions are not working, and
in the infamous list of non working extensions there where the three that I'm
&lt;a href="https://extensions.gnome.org/accounts/profile/danigm"&gt;maintaining right now&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/danigm/spotify-ad-blocker"&gt;mute-spotify-ads&lt;/a&gt;, extension to mute the spotify app when it's playing ads.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/danigm/gnome-shell-calculator"&gt;calc&lt;/a&gt;, a simple calculator in the alt+F2 input.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/danigm/hide-minimized"&gt;hide-minimized&lt;/a&gt;, hide minimized windows from alt+tab and overview.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/GNOME45-desktop.webp" /&gt;
&lt;/p&gt;

&lt;h2&gt;The correct way&lt;/h2&gt;
&lt;p&gt;The correct way to maintain and update a gnome-shell extension should be to
test and update "before" the official release of GNOME. It should be something
easy for me, using Tumbleweed I can just add the &lt;a href="https://build.opensuse.org/project/show/GNOME:Next"&gt;GNOME Next&lt;/a&gt; repository,
install the new desktop when it's in beta stage, and update the extension
there.&lt;/p&gt;
&lt;p&gt;But that's something that require an active maintainership... And right now
what I do is to update the extensions when they are broken for me, so just when
I need them, and that's after I get the new desktop and I find some time to
update.&lt;/p&gt;
&lt;p&gt;I know that's not the correct way and this produces a bad experience for other
people using the extensions, but this is the easier thing to do for me. Maybe
in the future I can do it correctly, and provide a tested update before the
official release, maybe using snapper to be able to go back to stable, without
the need of using virtual machines.&lt;/p&gt;
&lt;h2&gt;Update your extension to GNOME 45&lt;/h2&gt;
&lt;p&gt;The update to GNOME 45 was a bit more complex than previous ones. This version
of the shell and gjs change a lot of things, so the migration it's not just to
add a new number to the metadata.json, but requires incompatible changes, so
extensions that works for GNOME 45 won't work for previous versions and
vice versa.&lt;/p&gt;
&lt;p&gt;But the people working in gnome-shell does a great work documenting and there's
a really nice guide about &lt;a href="https://gjs.guide/extensions/upgrading/gnome-shell-45.html"&gt;how to upgrade your extension&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The most important part is the import section, that now it has a different syntax.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Before&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GNOME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;
&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;imports&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GNOME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resource:///org/gnome/shell/ui/main.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And some changes in the extension class, that now can inherit from existing
ones to provide common usage, like preferences window:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Adw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;gi://Adw&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ExtensionPreferences&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gettext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;MyExtensionPreferences&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExtensionPreferences&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;fillPreferencesWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_settings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Adw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreferencesPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Adw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreferencesGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Group Title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&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;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 03 Nov 2023 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2023-11-03:/gnome-45-extensions.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>linux</category><category>javascript</category></item><item><title>One year of Tumbleweed</title><link>https://danigm.net/tumbleweed.html</link><description>&lt;p class="img"&gt;
    &lt;a target="_blank" href="https://www.opensuse.org/#Tumbleweed"&gt;
        &lt;img src="/pictures/tumbleweed.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;More than a year has passed since I switched to &lt;a href="https://www.opensuse.org/#Tumbleweed"&gt;openSUSE Tumbleweed&lt;/a&gt;
Linux distribution, in both, my work computer (for obvious reasons)
and in my personal computer and I can say that I'm really happy with
the change.&lt;/p&gt;
&lt;p&gt;Tumbleweed is a &lt;a href="https://en.wikipedia.org/wiki/Rolling_release"&gt;rolling release&lt;/a&gt; distribution, and in this kind of
distributions there are a lot of changes every week, if you want the
latest software, this kind of distribution is the way to go. But with
high update frequency you are exposed to some kind of instability,
it's impossible to have the latest changes without some broken program
here and there, because not everyone is able to follow upstream
changes without some weeks or months to update.&lt;/p&gt;
&lt;h2&gt;My distro history&lt;/h2&gt;
&lt;p&gt;I've been always a Linux user, since I get my first computer at 2003.
In those days I was using a &lt;a href="https://www.debian.org/"&gt;debian&lt;/a&gt; base distribution called
&lt;a href="https://www.knopper.net/knoppix/index-en.html"&gt;knoppix&lt;/a&gt;. Then I switched to &lt;a href="https://ubuntu.com/"&gt;ubuntu&lt;/a&gt; when it appeared around 2004.
But at that time I was a computer science student and I was exploring
the whole free software and Linux ecosystem, so I was changing my
distribution every time that I found a new one.&lt;/p&gt;
&lt;p&gt;Like a lot of distro-hoppers, at some point I landed at &lt;a href="https://archlinux.org/"&gt;ArchLinux&lt;/a&gt;
and there I discovered the rolling release concept. And that was my
home for some time, it was nice to have the latest available software
just after the release.&lt;/p&gt;
&lt;p&gt;At some point I bough a new computer and it was too new to work
correctly with the kernel distributed in ArchLinux, so I tried
different distributions and at that moment &lt;a href="https://fedoraproject.org/"&gt;Fedora&lt;/a&gt; was the distro
that works without too much complications with that computer, so I
picked that one.&lt;/p&gt;
&lt;p&gt;In 2019 I started to work at Endless and at that time I should try the
&lt;a href="https://www.endlessos.org/"&gt;EndlessOS&lt;/a&gt;, so I played a bit with &lt;a href="https://danigm.net/endlessos-dual-boot.html"&gt;the dual boot&lt;/a&gt;, having
Fedora and EndlessOS at the same time. That was the first time that I
get in contact with immutable distributions, something that's getting
more popular everyday, but this distributions rely a lot on containers
(flatpak, podman) and, even being something that could work, as a
software engineer, I don't feel comfortable enough needing a container
with another distribution to do something that could be in my system.&lt;/p&gt;
&lt;p&gt;In 2022 I started to &lt;a href="https://danigm.net/suse.html"&gt;work at SUSE&lt;/a&gt; and for the first time I tried
the openSUSE distribution until today.&lt;/p&gt;
&lt;h2&gt;The Tumbleweed&lt;/h2&gt;
&lt;p&gt;Today I've three different computers with Tumbleweed running. One for
work, Thinkpad T14s, one for personal usage, Dell inspiron 5490, and
another one as a personal media server, Libre computer &lt;a href="https://libre.computer/products/aml-s805x-ac/"&gt;La-frite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The best thing of having Tumbleweed for me is that I get the latest
&lt;strong&gt;GNOME&lt;/strong&gt; as soon as it's released. And another big thing for this
distribution is how easy it's to fix something upstream thanks to the
&lt;a href="https://build.opensuse.org/project/show/openSUSE:Factory"&gt;Open Build Service&lt;/a&gt;, but I work everyday with that, so I'm
biased. For sure, any other community distribution has different ways
to contribute, but I find this one easy enough.&lt;/p&gt;
&lt;p&gt;Even being a rolling release distro, Tumbleweed doesn't break a lot. I
can't say that it's stable, because the API of everything is broken
everyday, but the &lt;a href="https://openqa.opensuse.org/group_overview/1"&gt;distribution is tested&lt;/a&gt; for every release and
at least some level of package compatibility check is done. That makes
Tumbleweed a good distribution and I can update without fearing some
weird package breakage.&lt;/p&gt;
&lt;p&gt;I usually update my work and personal laptops once a week, and
la-frite not so often, maybe every 6 months.&lt;/p&gt;
&lt;p&gt;With the default installation, Tumbleweed uses btrfs with snapshots,
and it's really easy to go back and forward using the
&lt;a href="https://en.opensuse.org/openSUSE:Snapper_Tutorial"&gt;snapper tool&lt;/a&gt;. So it's really easy to go back to a good state if
the distribution is broken for some reason, and wait for a fix.&lt;/p&gt;
&lt;h2&gt;The problems that I found during this year&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Some problems with the NVidia graphic card in my Dell laptop, some
   times the kernel and the driver were not working correctly. I had
   to use snapper to get the NVidia working again, but fixed a few
   days later.&lt;/li&gt;
&lt;li&gt;Currently I'm having some random crashes because some bug with
   &lt;a href="https://bugzilla.suse.com/show_bug.cgi?id=1215695"&gt;amdgpu&lt;/a&gt; and wayland and mutter, but it's not too annoying for
   me to go back, so I didn't use snapper this time and I'm facing
   this random crashes waiting for the fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Long live the Tumbleweed&lt;/h2&gt;
&lt;p&gt;So far so good. Tumbleweed is a nice distribution that I'm enjoying.
It's not getting in the way and I can find almost anything that I need
for work, programming, gaming, media, etc. I'm really happy with this
distribution and it's the perfect distribution for people like me,
that want to have the latest things.&lt;/p&gt;
&lt;p&gt;I know that there are other openSUSE flavors that are interesting,
like the &lt;a href="https://en.opensuse.org/Portal:Aeon"&gt;immutable ones&lt;/a&gt;, &lt;a href="https://en.opensuse.org/openSUSE:Slowroll"&gt;Leap&lt;/a&gt; or the latest one
&lt;a href="https://en.opensuse.org/openSUSE:Slowroll"&gt;Slowroll&lt;/a&gt;, but Tumbleweed is the one for me.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 06 Oct 2023 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2023-10-06:/tumbleweed.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>openSUSE</category><category>tumbleweed</category><category>linux</category><category>distribution</category></item><item><title>rpmlint updates (August 2023)</title><link>https://danigm.net/rpmlint-updates-2023-08-30.html</link><description>&lt;p&gt;We are at the end of the summer and this means that this year Google Summer of
code is ending.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/rpmlint-gitg-gsoc-2023.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The recent changes applied now in the main branch include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remove usage of &lt;code&gt;pkg_resource&lt;/code&gt; because it's deprecated.&lt;/li&gt;
&lt;li&gt;Fix elf binary check with ELF files with a prefix.&lt;/li&gt;
&lt;li&gt;New check for python packages with multiple .pyc files for different python
   versions.&lt;/li&gt;
&lt;li&gt;Improve the testing framework (merged the work done during the GSoC 2023)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summer of Code 2023 updates&lt;/h2&gt;
&lt;p&gt;The summer of code is ending and the work done by &lt;a href="https://github.com/rpm-software-management/rpmlint/pull/1101"&gt;Afrid was good enough to be merged&lt;/a&gt;,
so I merged it the past week.&lt;/p&gt;
&lt;p&gt;I'm really happy with the work done during the GSoC program, now we've a more
simple way to define tests for rpmlint checks mocking the rpm, so it's not
always needed to build a fake rpm binary for each new test. This will make a
lot easier to create simple tests, so I hope that we can increase the code
coverage using this &lt;a href="https://github.com/rpm-software-management/rpmlint/blob/main/test/README.md"&gt;new framework&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;During this time Afrid has extended the &lt;code&gt;FakePkg&lt;/code&gt; class, so it's possible now
to define fake metadata and files with fake tags and attributes. It's not
complete and it's not a simple task to replace all the &lt;code&gt;rpm&lt;/code&gt; binaries used for
tests, because the &lt;code&gt;Pkg&lt;/code&gt; class and &lt;code&gt;RPM&lt;/code&gt; tags is a complex thing, but the
current state allow us to replace a lot of them. Afrid has replaced some of the
tests that uses binaries, but in the following months we can continue working
on this and &lt;a href="https://github.com/rpm-software-management/rpmlint/issues/1105"&gt;replace more&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After this work, we can now start to use more the &lt;code&gt;FakePkg&lt;/code&gt; class in tests,
so another task that we can do is to provide some &lt;a href="https://github.com/rpm-software-management/rpmlint/issues/1104"&gt;common fake pkgs&lt;/a&gt; to use
in different tests and new checks, so now it's possible to create fake packages
with dynamic random data, so we can extend tests with fuzz testing and maybe
this will help to improve the tool reliability.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/gsoc.png" /&gt;
&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I've participated as mentor several times now in the &lt;a href="https://summerofcode.withgoogle.com/"&gt;summer of code&lt;/a&gt;, and
&lt;a href="https://www.outreachy.org/"&gt;outreachy&lt;/a&gt;, and almost always was a good experience. With the &lt;a href="https://www.gnome.org/"&gt;gnome foundation&lt;/a&gt;
in previous programs and this year with &lt;a href="https://www.opensuse.org/"&gt;opensuse&lt;/a&gt;. These two communities
are very open to collaboration and makes the whole process really simple, for
me as mentor, and also for the intern.&lt;/p&gt;
&lt;p&gt;I want to congratulate Afrid, because it was nice to work with him during this
summer, he has done a great work, not just technically, but communicating,
asking and finding his own solutions without requiring a continuous guidance.&lt;/p&gt;
&lt;p&gt;He is very passionate and looks like a nice person, so I hope that he will
continue around the open source, it could be opensuse, rpmlint or any other
community, but this kind of people is what you want to find in any community.&lt;/p&gt;
&lt;p&gt;After many years collaborating with different free software communities, it's
amazing that there are so many great people in every project, of course you can
find toxic communities and people, but in my experience, that's usually just
noise, there are a lot of nice people out there, doing a great work, and I'm
happy that young people like Afrid can be part of the free software movement,
because this is what makes the free software great, the people that is
working on it.&lt;/p&gt;
&lt;p&gt;So Thanks a lot to Google for another summer of code, thanks to SUSE for
letting me, and encourage me, to mentor, and thanks to all the free software
developers that are out there.&lt;/p&gt;
&lt;p&gt;I encourage everyone to participate in this kind of programs, for interns, it's
a good opportunity to learn and to make some money working on free software,
for mentors it's an opportunity to get some help in your project and help
newcomers to be part of the community.&lt;/p&gt;
&lt;p&gt;Have a lot of fun!&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Wed, 30 Aug 2023 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2023-08-30:/rpmlint-updates-2023-08-30.html</guid><category>blog</category><category>gnome</category><category>software</category><category>rpmlint</category><category>suse</category><category>gsoc</category></item><item><title>Python in openSUSE Tumbleweed</title><link>https://danigm.net/python-tw.html</link><description>&lt;p&gt;&lt;a href="https://www.opensuse.org/#Tumbleweed"&gt;openSUSE Tumbleweed&lt;/a&gt; is a rolling release distribution, so it's
ideal for developers and users that like to have the bleeding edge
software.&lt;/p&gt;
&lt;p&gt;It's also really "stable" to be a rolling release, from time
to time you can find a broken package because of one package is
updated and another one is not compatible yet, but it's something that
doesn't happen too often thanks to &lt;a href="https://openqa.opensuse.org/group_overview/1"&gt;openqa tests&lt;/a&gt;, so you don't
need to worry about a breaking system.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/python-tw.png" /&gt;
&lt;/p&gt;

&lt;p&gt;You can find the &lt;a href="https://www.python.org/"&gt;Python interpreter&lt;/a&gt; and a lot of &lt;a href="https://pypi.org/"&gt;python modules&lt;/a&gt;
in every Linux distribution, but Tumbleweed does an interesting thing
for Python.&lt;/p&gt;
&lt;h2&gt;Default Python version (python3 -&amp;gt; python-3.11)&lt;/h2&gt;
&lt;p&gt;If you don't worry about the python version, you can just rely on
&lt;code&gt;python3&lt;/code&gt;. In Tumbleweed &lt;code&gt;python3&lt;/code&gt; is not a real package, but the
default python version provides &lt;code&gt;python3&lt;/code&gt;, so depending on when you
install &lt;code&gt;python3&lt;/code&gt; you will get a different package, if you install
it today (August 2023) you'll get &lt;code&gt;python311&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In your system you'll have the &lt;code&gt;/usr/bin/python3&lt;/code&gt; binary that points
to the default python, so you don't need to worry about the current
version, you'll have there the default Python version for the
operating system.&lt;/p&gt;
&lt;p&gt;In addition to the Python interpreter, you can find in the
distribution a lot of python modules, but again you can use the
default version, so, for example, if you want to install &lt;code&gt;poetry&lt;/code&gt;, you
just use &lt;code&gt;zypper install python3-poetry&lt;/code&gt; and that will install
the real package &lt;code&gt;python311-poetry&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Multiple python versions (3.8, 3.9, 3.10, 3.11)&lt;/h2&gt;
&lt;p&gt;Besides the default Python, in Tumbleweed you can also find other
supported Python interpreters. Right now you can find all the Python
versions currently supported by the Python Foundation, from 3.8 to
3.11.&lt;/p&gt;
&lt;p&gt;For Python 3.8 you'll only find the interpreter, because the python
modules are not built anymore, but for all the other versions you can
find almost the same modules.&lt;/p&gt;
&lt;p&gt;All python modules that provide binaries uses the
&lt;code&gt;update-alternatives&lt;/code&gt;, so you can configure in your system the version
that you want to use as default. For example, if you want to use the
&lt;code&gt;3.9&lt;/code&gt; version of &lt;code&gt;poetry&lt;/code&gt;, having installed different versions you can
decide what &lt;code&gt;/usr/bin/poetry&lt;/code&gt; points to:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;zypper&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python311-poetry&lt;span class="w"&gt; &lt;/span&gt;python39-poetry
$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;update-alternatives&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;poetry
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;/usr/bin/python3&lt;/code&gt; is a link provided by the default python package,
so you can't modify with &lt;code&gt;update-alternatives&lt;/code&gt;, so if you want to use
a different python version, make sure to do the correct call with the
full name &lt;code&gt;python3.9&lt;/code&gt;, and use the correct shebang in your python
scripts, for example &lt;code&gt;#!/usr/bin/env python3.9&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;What happens when default changes?&lt;/h2&gt;
&lt;p&gt;This way of distributing Python interpreter and modules is useful,
because you don't need to update your software to work with the latest
Python version, if your software requires another version, you can just
install and continue using it, even on a bleeding-edge distribution
like Tumbleweed.&lt;/p&gt;
&lt;p&gt;But this method has some problems. When the default Python interpreter
is changed in the distribution, all packages that depends on &lt;code&gt;python3&lt;/code&gt;
will be updated, and that works correctly. But if you have installed
some python module using the &lt;code&gt;python3&lt;/code&gt; prefix, that package, and all
dependencies, is not updated automatically.&lt;/p&gt;
&lt;p&gt;For example, if you installed &lt;code&gt;python3-poetry&lt;/code&gt; when &lt;code&gt;python3.10&lt;/code&gt; was
the default system, and then the distribution updates the system
Python to &lt;code&gt;python3.11&lt;/code&gt;, you don't get the &lt;code&gt;python311-poetry&lt;/code&gt; package
by default, you'll need to install it again. This could break you
software, because if you use &lt;code&gt;python3&lt;/code&gt; and the dependencies are not
updated, you'll find that some dependencies are not installed after
updating.&lt;/p&gt;
&lt;p&gt;For that reason, when the default python is changed in Tumbleweed, if
you've software that rely on &lt;code&gt;python3&lt;/code&gt; you should make sure to install
dependencies again by hand. And also you can do a cleanup and remove
all old version packages that you don't need anymore.&lt;/p&gt;
&lt;p&gt;If you just want the latest version you can just do all at once with a
simple script like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c1"&gt;# up-py-tw.sh&lt;/span&gt;

&lt;span class="nv"&gt;FROM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;TO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-lt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Usage:   up-py-tw.sh FROM TO&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example: up-py-tw.sh 310 311&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Updating python packages from &lt;/span&gt;&lt;span class="nv"&gt;$FROM&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="nv"&gt;$TO&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="nv"&gt;OLDP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;zypper&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FROM&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;-*&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tail&lt;span class="w"&gt; &lt;/span&gt;--lines&lt;span class="w"&gt; &lt;/span&gt;+4&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;--delimiter&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--fields&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;NEWP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OLDP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;s/python&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FROM&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/python&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TO&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/g&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;

sudo&lt;span class="w"&gt; &lt;/span&gt;zypper&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$NEWP&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;zypper&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OLDP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should verify what will be installed and what will be removed to
make sure that just the python related packages are removed. It's
possible that other packages depend on a python version that's older
than the system one and that's okay.&lt;/p&gt;
&lt;h2&gt;Beta version (3.12.0b4)&lt;/h2&gt;
&lt;p&gt;In Tumbleweed, right now you can find all the supported Python
versions, but it's not just that. At this moment you can also find the
beta version of the next Python interpreter.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;zypper&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python312
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So if you are adventurous and want to test some new feature in the
next Python release, you can do it with the version provided there.
This version could also be used by openSUSE packagers to test the
packages before the Python version is released so it allow us to
prepare everything for the release and it could be in the distribution
earlier and more tested.&lt;/p&gt;
&lt;h2&gt;Development&lt;/h2&gt;
&lt;p&gt;All this multiple python versions is done at distribution level and
usually there's only one source package that produces the
&lt;code&gt;python39-foo&lt;/code&gt;, &lt;code&gt;python310-foo&lt;/code&gt; and &lt;code&gt;python311-foo&lt;/code&gt;. The source
package is usually called &lt;code&gt;python-foo&lt;/code&gt; and can be found in the
&lt;a href="https://build.opensuse.org/project/show/devel:languages:python"&gt;Python devel project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These packages spec uses the python-rpm-macros to generate all the
versions from one source. If you're a packager you can find more
information on the openSUSE wiki about &lt;a href="https://en.opensuse.org/openSUSE:Packaging_Python"&gt;Python Packaging&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 05 Aug 2023 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2023-08-05:/python-tw.html</guid><category>blog</category><category>software</category><category>suse</category><category>tumbleweed</category><category>gnome</category></item><item><title>rpmlint updates (July 2023)</title><link>https://danigm.net/rpmlint-updates.html</link><description>&lt;p&gt;I'm spending some time every week working in the &lt;a href="https://github.com/rpm-software-management/rpmlint"&gt;rpmlint&lt;/a&gt; project.
The tool is very stable and the functionality is well defined,
implemented and tested, so there's no crazy development or a lot of
new functionalities, but as in all the software, there are always bugs
to solve and things to improve.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/rpmlint-gitg-20230706.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The recent changes applied now in the main branch include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update the usage of &lt;code&gt;rpm&lt;/code&gt; to not use old API.&lt;/li&gt;
&lt;li&gt;Fixes for &lt;code&gt;rpmdiff -v&lt;/code&gt;, check for NULL char, special macros in
   comments and spell checking of description in different languages.&lt;/li&gt;
&lt;li&gt;Move all the metadata from &lt;code&gt;setup.py&lt;/code&gt; to &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Releasing rpmlint as pre-commit hook&lt;/li&gt;
&lt;li&gt;Improvements to the PythonCheck in the dependency checking.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summer of Code 2023 updates&lt;/h2&gt;
&lt;p&gt;The first month of the Summer of Code has passed and &lt;a href="https://afridhussain.tech"&gt;Afrid&lt;/a&gt; is
doing a great job there. We've now a &lt;a href="https://github.com/rpm-software-management/rpmlint/pull/1079"&gt;draft Pull Request&lt;/a&gt; with some
initial changes that allow us to mock &lt;code&gt;rpm&lt;/code&gt; packages in tests so it's
easier to create new tests without the need of creating a binary
package.&lt;/p&gt;
&lt;p&gt;The first step done was to extend the existing &lt;code&gt;FakePkg&lt;/code&gt; class to
allow us to define package files and some package metadata.&lt;/p&gt;
&lt;p&gt;Now he's working in replacing all of the &lt;code&gt;test_python.py&lt;/code&gt; tests that
uses binaries &lt;code&gt;rpm&lt;/code&gt; to something that doesn't needed.&lt;/p&gt;
&lt;p&gt;The idea is to replace as much tests as possible to reduce the number
of rpm binaries and after that, provide helper functions, decorators
and classes to make it easy to write tests, writing less code.&lt;/p&gt;
&lt;h2&gt;Roadmap&lt;/h2&gt;
&lt;p&gt;In any software project there's always room for improvements, fixes
and enhancements. If the project is there for enough time, it's even
more critical to modernize the code to reduce the technical debt.&lt;/p&gt;
&lt;p&gt;My plan for 2023 is to improve the tests around rpmlint as much as
possible. First with the GSoC project, making it easier to write more
tests, improving the testing tools that we've. And after the summer,
improving the test coverage.&lt;/p&gt;
&lt;p&gt;There's also a tool that shares some of the ideas with rpmlint,
&lt;a href="https://github.com/rpm-software-management/spec-cleaner"&gt;spec-cleaner&lt;/a&gt;, it's also written in Python, so the next step,
after the tests improvements will be to take a deep look into the code
of these two tools and try to integrate in some way. Maybe it's
possible to refactor the common code into an external module, maybe we
can bring some ideas from spec-cleaner to rpmlint. Not sure yet, but
that'll be my next step.&lt;/p&gt;
&lt;p&gt;Don't forget that this is free software, so you can participate too!
If you find any issue in rpmlint or have an idea to improve it, don't
hesitate and &lt;a href="https://github.com/rpm-software-management/rpmlint/issues"&gt;create a new issue&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Thu, 06 Jul 2023 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2023-07-06:/rpmlint-updates.html</guid><category>blog</category><category>gnome</category><category>software</category><category>rpmlint</category><category>suse</category><category>gsoc</category></item><item><title>rpmlint: Google Summer of Code 2023</title><link>https://danigm.net/gsoc-2023.html</link><description>&lt;p&gt;I'm glad to say that I'll participate again in the &lt;a href="https://summerofcode.withgoogle.com/"&gt;GSoC&lt;/a&gt;, as
mentor. This year will be a bit different from the previous ones,
because I'm not mentoring a GNOME project but a &lt;a href="https://www.opensuse.org/"&gt;openSUSE&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;I started to work at SUSE the past year and with this new job I get
involved in the openSUSE community and I started to contribute to
&lt;a href="https://github.com/rpm-software-management/rpmlint"&gt;rpmlint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So this summer I'll be mentoring an intern and we'll work on improving
the testing framework of the &lt;a href="https://github.com/openSUSE/mentoring/issues/189"&gt;rpmlint project&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/rpmlint.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The rpmlint project is a command line tool to check rpm packages, the
correctness of these kind of packages and warn the packagers about
usual problems or good practices. It's widely used in all Linux
distributions based on rpm, mainly SUSE and RedHat.&lt;/p&gt;
&lt;p&gt;It is written in Python and uses pytest for testing the code. Right
now there are a lot of .rpm binary packages, to check different
functionality, but that way of testing makes a bit hard to write new
tests and to maintain with changes. The idea of this GSoC project is
to extend the testing framework of rpmlint to support an easy way
of writting tests that doesn't require a real rpm, something that can
mock what it's in the .rpm binary and try to replace some of the
current binary tests with this new mock.&lt;/p&gt;
&lt;p&gt;The selected intern is &lt;a href="https://afridhussain.tech/post/accepted-into-gsoc/"&gt;Afrid Hussain&lt;/a&gt;. He has done some initial
work in the rpmlint project, solving some minor issues and we're now
preparing the work to be done during the GSoC program. I'm sure that
he will be able to achieve great things during these three months, so
I'm looking forward to start to code and see how far can we go.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Tue, 16 May 2023 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2023-05-16:/gsoc-2023.html</guid><category>blog</category><category>gnome</category><category>software</category><category>rpmlint</category><category>suse</category><category>gsoc</category></item><item><title>Hackweek 2023</title><link>https://danigm.net/hackweek22.html</link><description>&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/hackweek.png" width="50%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://hackweek.opensuse.org/"&gt;Hack Week&lt;/a&gt; is the time SUSE employees experiment, innovate &amp;amp; learn
interruption-free for a whole week! Across teams or alone, but always without
limits.&lt;/p&gt;
&lt;p&gt;This year the Hack Week was this week, the last week of January and for my
first SUSE hack week I decided to work in something funny, &lt;a href="https://hackweek.opensuse.org/22/projects/linux-immersive-learning-system-lils"&gt;LILS&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Linux Immersive Learning System (LILS)&lt;/h2&gt;
&lt;p&gt;I don't think that this is a good name, but don't focus on it. The main idea of
this project is to create some basic machinery to be able to write
"interactive" tutorials or games using the &lt;a href="https://www.inklestudios.com/ink/"&gt;INK language&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is not an original idea, indeed all I've done is something that's
currently working on &lt;a href="https://www.endlessos.org/"&gt;EndlessOS&lt;/a&gt;, and was the main idea behind the dead
project &lt;a href="https://www.hack-computer.com/"&gt;Hack Computer&lt;/a&gt;, you can even take a look to the
&lt;a href="https://flathub.org/apps/details/com.hack_computer.Clubhouse"&gt;Hack app in flathub&lt;/a&gt;. But I wanted to work around this, and create
something simpler, from scratch.&lt;/p&gt;
&lt;p&gt;I wanted to build something simple, with just Python, and make it simple enough
to be able to build other tools on top. The design is simple, an INK parser,
with a simple game runner. In the INK script you can define &lt;em&gt;commands&lt;/em&gt;, to do
something special, and wait for events with &lt;em&gt;listeners&lt;/em&gt;, to wait for an event
in the OS to continue.&lt;/p&gt;
&lt;p&gt;With this basic functionality it's possible to build different user interfaces
for different environments. And the original idea was to make the &lt;em&gt;commands&lt;/em&gt;
and &lt;em&gt;listeners&lt;/em&gt; something extensible with a simple API, but that's something
that I have not done yet, it's all Python functions without extension point.&lt;/p&gt;
&lt;p&gt;The code can be found in &lt;a href="https://github.com/danigm/lils"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The INK parser&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/inky.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The most complex part of this project is the INK language parser. The &lt;a href="https://github.com/inkle/ink"&gt;Ink&lt;/a&gt;
parser is free software and there's a Linux version that you can use to parse
and &lt;strong&gt;compile&lt;/strong&gt; to &lt;em&gt;json&lt;/em&gt;, but I wanted to create my own parser with Python.&lt;/p&gt;
&lt;p&gt;I've spent most of the Hack Week time fighting with the parser and indeed was
the most challenging and fun part, because I've not worked a lot with parsers
and it's not something easy as pie 😛️.&lt;/p&gt;
&lt;p&gt;I remember creating a java compiler long time ago, when I was in the Seville
University, for the Language Processors course. We did that with &lt;a href="https://www.antlr.org/"&gt;ANTLR&lt;/a&gt;, so
starting from that, and looking for a Python lib, I found the &lt;a href="https://github.com/lark-parser/lark"&gt;Lark&lt;/a&gt;
project. So if you like regular expressions, writing a grammar is a lot more
FUN.&lt;/p&gt;
&lt;p&gt;At the end I was able to support some basic INK language with support for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Text&lt;/li&gt;
&lt;li&gt;Tag support&lt;/li&gt;
&lt;li&gt;Options, with suppress text support&lt;/li&gt;
&lt;li&gt;Knots, Stitches and Diverts&lt;/li&gt;
&lt;li&gt;Include other .ink files&lt;/li&gt;
&lt;li&gt;Variable definition and basic operations&lt;/li&gt;
&lt;li&gt;Knots and Stitches automatic visiting count variables&lt;/li&gt;
&lt;li&gt;Conditional options using variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It still fails in some cases, the comments and &lt;em&gt;TODO&lt;/em&gt; placed in between text is
not detected correctly and there's a lot of complex stuff that's not supported
yet, but with what's supported right now it's possible to create complex
scripts with loops and complex game graphs, so it's good enough to build games
just with it.&lt;/p&gt;
&lt;h2&gt;GNOME shell extension&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;video src="/pictures/lils.mp4" width="100%" autoplay controls loop /&gt;
&lt;/p&gt;

&lt;p&gt;To integrate with the system I've done a simple &lt;a href="https://github.com/danigm/lils/tree/master/lils%40danigm.net"&gt;GNOME shell extension&lt;/a&gt;.
The extension just shows the text as bubbles and options as buttons, it's
really simple and I've no time to make it something ready to be used, but
I was able to make something usable.&lt;/p&gt;
&lt;p&gt;To be able to run the &lt;em&gt;LILS&lt;/em&gt; python library from &lt;em&gt;gjs&lt;/em&gt; I've created a simple
&lt;em&gt;dbus&lt;/em&gt; service that exposes the basic &lt;em&gt;InkScript&lt;/em&gt; class functionality as a dbus
API.&lt;/p&gt;
&lt;p&gt;I was thinking about being able to change the desktop background, depending of
the value of a &lt;em&gt;background&lt;/em&gt; variable in the script and do something similar to
play music and sounds, so it could be a cool &lt;em&gt;game engine&lt;/em&gt; with some additions.&lt;/p&gt;
&lt;h2&gt;SUSE Hack Week&lt;/h2&gt;
&lt;p&gt;So this Hack week was really fun and I learned a lot. It's really great that
SUSE does things like this, letting us work in different projects for a week,
to learn, to grow or to just explore different paths.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/IxscORgeqOY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen&gt;&lt;/iframe&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Tue, 03 Jan 2023 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2023-01-03:/hackweek22.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>ink</category><category>python</category></item><item><title>The end of 2022</title><link>https://danigm.net/2022.html</link><description>&lt;p class="img"&gt;
    &lt;img src="/pictures/2023.png" /&gt;
&lt;/p&gt;

&lt;p&gt;Is the end of 2022 and the beginning of 2023, so it's the right time to look
back and see what great things happened during the year, and also it's the time
to plan some new year resolutions.&lt;/p&gt;
&lt;h2&gt;My contribution to GNOME in 2022&lt;/h2&gt;
&lt;p&gt;I've been focused this year on the &lt;a href="https://gitlab.gnome.org/GNOME/gtranslator"&gt;GNOME Translation Editor&lt;/a&gt;, migrating it
to Gtk4. It's not ready yet, but I hope I will be able to have a working
version soon™.&lt;/p&gt;
&lt;p&gt;It started this summer with one intern from &lt;a href="https://www.outreachy.org/outreachy-may-2022-internship-round/#gnome-migrate-gnome-translation-editor-to-gtk4"&gt;Outreachy&lt;/a&gt;, that did some
initial work, building with the new Gtk4, and after the summer I started to
clean the code and fixing issues, and lately, I'm trying to replace some
deprecations for the next Gtk release.&lt;/p&gt;
&lt;p&gt;so I'm working on the code, modernizing it, and replacing every usage of
&lt;code&gt;GtkDialog&lt;/code&gt; and &lt;code&gt;GtkTreeView&lt;/code&gt;, and trying to follow the &lt;a href="https://developer.gnome.org/hig/"&gt;GNOME HIG&lt;/a&gt; as much
as possible.&lt;/p&gt;
&lt;h3&gt;Live coding Streaming&lt;/h3&gt;
&lt;p&gt;I started the year spending a lot of time working on Gtk on live stream on
&lt;a href="https://www.twitch.tv/abentogil/"&gt;Twitch&lt;/a&gt;, but after the summer I was spending less time... I didn't find the
time or the energy to do it regularly.&lt;/p&gt;
&lt;h2&gt;Changes in the business&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/endless-photo.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;This year was also a year of changes in my work life. After the summer I left
Endless, after three years of working there, to join SUSE.&lt;/p&gt;
&lt;p&gt;I really enjoy my time at &lt;a href="https://www.endlessos.org/"&gt;EndlessOS&lt;/a&gt;, the first years working on the Hack
Computer and the last year working on the Endless Key. I really like the
EndlessOS mission and what I was doing there, with a lot of great people.&lt;/p&gt;
&lt;p&gt;But I was looking for something more in my career path, and I had the
opportunity to join SUSE. Since September, I'm part of the Python packaging
team at SUSE and I've to say that I really love this job. It's a bit different
from what I have been doing before, but the maintainer life is something that I
enjoy, having the opportunity to contribute to a lot of different projects and
debugging and fixing random bugs is one of the most rewarding tasks to do (when
you are able to find the problem).&lt;/p&gt;
&lt;p&gt;Tumbleweed is now my favourite GNU/Linux distribution and the future of
&lt;a href="https://news.opensuse.org/2022/12/22/second-prototype-advances-alp/"&gt;SUSE ALP&lt;/a&gt; looks really promising.&lt;/p&gt;
&lt;h2&gt;Changes in life&lt;/h2&gt;
&lt;p&gt;This year comes also with a lot of changes in my personal life too! At the
beginning of the year I was able to bought a house, so I've now a permanent
residency, after a lot of years of changing from one rent house to another one.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/blackbelt.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;And this year was also the year that I was able to get the kickboxing black
belt, after almost 8 years of practicing. This is the end of a learning path,
and also the beginning of a different one, trying to master this fighting
sport.&lt;/p&gt;
&lt;h2&gt;New year resolutions&lt;/h2&gt;
&lt;p&gt;2022 was great, but we can make 2023 even better, I'm looking always to learn
and improve, so it's good to do some new year resolutions to try to complete
during the next year.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complete the Gtranslator migration to gtk4 and GNOME HIG improvements!&lt;/li&gt;
&lt;li&gt;Be more regular with the live coding streams&lt;/li&gt;
&lt;li&gt;Give some love to the &lt;a href="https://www.bassi.io/articles/2022/12/02/on-pygobject/"&gt;PyGObject&lt;/a&gt; project&lt;/li&gt;
&lt;li&gt;Do more serious kickboxing training and loose 10Kg&lt;/li&gt;
&lt;li&gt;Play more chess games&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's all! Happy new year and Have a lot of fun...&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 30 Dec 2022 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2022-12-30:/2022.html</guid><category>blog</category><category>gnome</category><category>work</category><category>2022</category><category>newyear</category></item><item><title>SUSE is my new distribution (new job)</title><link>https://danigm.net/suse.html</link><description>&lt;p class="img"&gt;
    &lt;a target="_blank" href="https://suse.com"&gt;
        &lt;img src="/pictures/Poster-Always-Changing.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;This week I've started to work at &lt;a href="https://suse.com/"&gt;SUSE&lt;/a&gt;. I'll be working as Python
Specialist, in the packaging team, so I will go back to work on packaging and
distribution after more than ten years. My first job in 2008 was working on a
Ubuntu based local distribution, &lt;a href="https://en.wikipedia.org/wiki/Guadalinex"&gt;Guadalinex&lt;/a&gt;, so packaging and distribution
work is not something new for me.&lt;/p&gt;
&lt;h2&gt;Python&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; was the first language that I fell in love. I learned to write code
with C and C++, but when I discovered Python, in 2006, I found a really nice
language to be able to create amazing things really fast and with a great
community behind.&lt;/p&gt;
&lt;p&gt;I'm very happy for this new opportunity to be able to collaborate to the Python
distribution in all the SUSE flavours, and also to be able to collaborate in
the creation of one of the most famous and used Linux distributions.&lt;/p&gt;
&lt;h2&gt;Tumbleweed&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;a target="_blank" href="https://www.opensuse.org/#Tumbleweed"&gt;
        &lt;img src="/pictures/tumbleweed.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;As part of this job change I've also installed &lt;a href="https://www.opensuse.org/#Tumbleweed"&gt;SUSE Tumbleweed&lt;/a&gt; for the
first time. Tumbleweed is a rolling release distribution with the latests
packages. In the past I was using other rolling releases distributions like
Arch, but this one looks more user friendly.&lt;/p&gt;
&lt;p&gt;I've not spent a lot of time here, but from the point of view of a GNOME
developer, I can say that it's a great distribution for development with
updated packages, and it looks "stable". You can choose the desktop to use on
installation and the GNOME desktop is there without any customization that I've
detected, so it looks like it's a good vanilla GNOME desktop distribution.&lt;/p&gt;
&lt;h2&gt;Endless, it's not the end&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;a target="_blank" href="https://endlessos.org"&gt;
        &lt;img src="/pictures/endless-farewell.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I'm not working for &lt;a href="https://endlessos.org"&gt;EndlessOS&lt;/a&gt; now, but it's not the end. I've been working
here for almost 4 years. At first I worked on the &lt;a href="https://hack-computer.com/"&gt;Hack Computer&lt;/a&gt; and after
that project didn't work, I was working on the &lt;a href="https://www.endlessos.org/key"&gt;Endless Key&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;During this time I've also collaborated a bit with the EndlessOS distribution,
and I can say that's a really nice distribution to use, the ostree usage for
the whole filesystem is a great idea, and the amount of content that comes with
the installation is really good.&lt;/p&gt;
&lt;p&gt;The EndlessOS Foundation Goal is to reduce the digital divide, providing
content and tools for offline people, centered on kids. This is a great mission
and in the future, if I find the opportunity to help my local community, I'll
try to use the EndlessOS tools and content to provide good learning content for
kids without online access.&lt;/p&gt;
&lt;p&gt;I was very happy these years at Endless, and I've learned a lot from different
great people. It's incredible the number of talented software engineers that
are related to Endless, and for me it was a real privilege to be able to share
this space and mission for a few years.&lt;/p&gt;
&lt;h2&gt;The future!&lt;/h2&gt;
&lt;p&gt;So there we go, I'm exited for this change, and also sad about leaving a great
project, but life is change and we should go ahead and think about the future!
And my future is green now.&lt;/p&gt;
&lt;p&gt;And if you don't know how to pronounce it, here you've a music video:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/nLdexZlVkAY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sun, 18 Sep 2022 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2022-09-18:/suse.html</guid><category>blog</category><category>gnome</category><category>work</category><category>endless</category><category>suse</category></item><item><title>Berlin Mini GUADEC 2022</title><link>https://danigm.net/berlin-mini-guadec.html</link><description>&lt;h2&gt;The GUADEC is back!&lt;/h2&gt;
&lt;p&gt;This year, the famous GNOME developers meeting event, the &lt;a href="https://events.gnome.org/event/77/"&gt;GUADEC&lt;/a&gt;, gets
back to "normal" after the last year global COVID-19 situation. And when I say
normal, I'm talking about people meeting in one place to share knowledge and to
build a great community around this great software project.&lt;/p&gt;
&lt;p&gt;This year the even was in Guadalajara, Mexico, and that's great, but a bit far
to go from Spain... But thankfully, some of GNOME folks who can't go to Mexico
organized a parallel event to attend remotely in Berlin, Europe, that's the
Berlin Mini GUADEC.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/berlin-mini-guadec/plane.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;I decided to join this people, because there's a direct flight from Málaga, so
it was not a very long travel for me. This was my first big trip after COVID, and
I was a bit afraid, but besides the heat wave, everything was great.&lt;/p&gt;
&lt;h2&gt;My talk&lt;/h2&gt;
&lt;p&gt;The GUADEC event allows a remote attendance and also remote participation, so I
proposed &lt;a href="https://events.gnome.org/event/77/contributions/351/"&gt;a talk&lt;/a&gt; that I finally did from Berlin thanks to the &lt;a href="https://www.c-base.org/"&gt;c-base&lt;/a&gt;
people that build a great setup to give the talk to the local people at the
same time that it was streamed to the main event in Mexico.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/berlin-mini-guadec/gnome-streamers-2022.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;As you may know, I started to do &lt;a href="https://danigm.net/twitch.html"&gt;live coding streaming&lt;/a&gt; this year, and
there are also other &lt;a href="https://wiki.gnome.org/Community/Streamers"&gt;GNOME Developers&lt;/a&gt; doing that, so I wanted to talk
about this new way of sharing and build community, and that was my talk about.&lt;/p&gt;
&lt;p&gt;You can watch my full talk in Youtube:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/wuFTiAcdBXk?start=9346" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h2&gt;Endless&lt;/h2&gt;
&lt;p&gt;It's not all party and fun in this event, there's also time to work. We take
advantage of this event and some of the Endless people take the opportunity to
meet together.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/berlin-mini-guadec/endless-family-2.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;We're fully remote and I started to work at Endless in 2019. We had the
opportunity to meet one time in London, but after that, this is the second time
we had the opportunity to share some time together and it was great. Besides
the day to day work, that can be done in remote completely, it's always nice to
have the opportunity to share some time with coworkers to meet the real person.&lt;/p&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;My travel was sponsored by the &lt;a href="https://www.gnome.org/foundation/"&gt;GNOME Foundation&lt;/a&gt;, and thanks to the
&lt;a href="https://www.endlessos.org/"&gt;EndlessOS Foundation&lt;/a&gt;, to give me the opportunity to be part of this great
community.&lt;/p&gt;
&lt;p style="text-align: center"&gt;
    &lt;a href="https://gnome.org"&gt;
        &lt;img src="/pictures/sponsored-by-foundation-round.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 22 Jul 2022 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2022-07-22:/berlin-mini-guadec.html</guid><category>blog</category><category>gnome</category><category>guadec</category><category>berlin</category><category>community</category><category>endless</category></item><item><title>Gambito, a chess App in Rust</title><link>https://danigm.net/gambito.html</link><description>&lt;h2&gt;Chess&lt;/h2&gt;
&lt;p&gt;I've been always interested in Chess, but never learned to play it correctly or
even spent some time playing more than a couple of matches with friends. But
during the COVID lockdown I started to watch people &lt;a href="https://www.twitch.tv/gmhikaru"&gt;playing chess&lt;/a&gt;, and
then I realized that the game is even more fun than I expected.&lt;/p&gt;
&lt;p&gt;After that I started to play a bit online and discovered the great online
platform &lt;a href="https://lichess.org/"&gt;Lichess&lt;/a&gt;, that's even &lt;a href="https://github.com/lichess-org"&gt;free software&lt;/a&gt;. I do not play a lot, but
I discovered that I like a lot the game and I watch chess streamers everyday
and I even followed the &lt;a href="https://fide.com/"&gt;professional competition&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So if you don't play chess, give it a try, it's a really nice board game, and a
beauty "video game" that you can play online in a complete free software platform
with a lot of community resources to learn, and even if you are a bad player like me,
you can always enjoy the beauty of the chess watching other people playing.&lt;/p&gt;
&lt;h2&gt;Gambito&lt;/h2&gt;
&lt;p&gt;With this new interesting hobby, I installed the chess application that I found
in gnome. &lt;a href="https://gitlab.gnome.org/GNOME/gnome-chess"&gt;GNOME Chess&lt;/a&gt; is a good application, but then I ask myself, why
not rewrite it in Rust? Okay, it's not a rewrite, it's just a new application,
my idea is not to replace GNOME Chess, but to experiment with Gtk4 and Rust,
and that's the reason I started to work on &lt;a href="https://gitlab.gnome.org/danigm/gambito"&gt;Gambito&lt;/a&gt;, that's just a new
Chess game for GNOME, written in Rust with Gtk4.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/gambito-screenshot01.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The first idea was to create a simple app, without widget inheritance or
anything, just draw a board with existing Gtk Widgets, and do the drawing with
CSS and the interaction with drag &amp;amp; drop. And I did that for almost all the
application, but at some point I needed something more specific that I was
unable to do without a new widget. So right now almost everything is done that
way but the marks and arrows, that are custom widgets.&lt;/p&gt;
&lt;p&gt;I'm doing almost all the development in live streaming (in Spanish),
&lt;a href="https://www.twitch.tv/abentogil/"&gt;in my twitch account&lt;/a&gt;, so you can find some of the videos here in my
&lt;a href="https://www.youtube.com/c/danigmx/search?query=gambito"&gt;youtube channel&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/gambito-screenshot03.png" /&gt;
&lt;/p&gt;

&lt;p&gt;Right now you can use the application to play against Stockfish or to analyse a
match. At the beginning the idea was to create just a chess game, but right now
I'm thinking more about a chess learning application, so I think I'll work more
in analysis tools, tactic training games, and maybe some good content for
beginners, inside the application, like a tutorial and some kind of assistant with
theory.&lt;/p&gt;
&lt;p&gt;And there's a lot of fun things to do, like linking with the Lichess platform,
to be able to watch games in live, play online using the app instead the web
interface, analyze Lichess matches and look for cheaters doing some kind of IA
(yes, there are cheaters using chess engines to win matches in online chess
platforms, I don't know why, but there are people like that).&lt;/p&gt;
&lt;p&gt;So I'm getting a lot of fun from the old game Chess, fun playing, fun watching
an fun writing a Chess game.&lt;/p&gt;
&lt;p&gt;Have fun!&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/gambito-logo.png" /&gt;
&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 09 Jul 2022 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2022-07-09:/gambito.html</guid><category>blog</category><category>gambito</category><category>chess</category><category>gtk4</category><category>gnome</category><category>rust</category><category>twitch</category></item><item><title>GNOME Outreachy 2022</title><link>https://danigm.net/outreachy-2022.html</link><description>&lt;h2&gt;GNOME Translation Editor, Road to Gtk4&lt;/h2&gt;
&lt;p&gt;It's time to move to Gtk4. That could be an easy task for new project or for
small projects without a lot of custom widgets, but &lt;a href="https://gitlab.gnome.org/GNOME/gtranslator"&gt;gtranslator&lt;/a&gt; is old and
the migration will require some time.&lt;/p&gt;
&lt;p&gt;Some time ago I did the &lt;a href="https://danigm.net/gtranslator-resurrection.html"&gt;Gtk2 to Gtk3&lt;/a&gt; migration. It was fun and during the
journey we redesigned a bit the interface, but the internals didn't change a
lot. Now we can do the same, migrate to Gtk4 and also update the User
Interface.&lt;/p&gt;
&lt;p&gt;Thankfully, I'm not alone this time, the GNOME community is there to help. A
couple of months ago, &lt;a href="https://gitlab.gnome.org/GNOME/gtranslator/-/commits/master?author=msandova"&gt;Maximiliano&lt;/a&gt; started a series of commits to prepare
the project to the Gtk4 migration, and today starts the Outreachy program and
we've a great intern to work in this. &lt;a href="https://www.outreachy.org/alums/2022-05/"&gt;Afshan Ahmed Khan&lt;/a&gt; will be working
during this summer in the GNOME Translation Editor migration to Gtk4.&lt;/p&gt;
&lt;h2&gt;Outreachy&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://www.outreachy.org/"&gt;Outreachy&lt;/a&gt; program provides internship to work in Free and Open Source
Software. This year I've proposed the "Migrate GNOME Translation Editor to Gtk4"
project and we had a lot of applicants. We had some great contributions during
the application phase, and at the end Afshan was selected.&lt;/p&gt;
&lt;p&gt;We've now an initial &lt;a href="https://dev.to/redoca2k/beginning-outreachy-journey-with-gnome-o8j"&gt;intern blog post&lt;/a&gt; and he is working now in the first
step, trying to build the project with Gtk4. It's not a simple task, because
gtranslator uses a lot of inheritance and there's a lot of widgets in the
project.&lt;/p&gt;
&lt;h2&gt;User Interface redesign?&lt;/h2&gt;
&lt;p&gt;Once we've the project working with Gtk4 and libadwaita we can start to think
about user interface improvements, and all the collaboration here is welcome,
so if some designer or translator want to help, don't hesitate to take a look
to the &lt;a href="https://flathub.org/apps/details/org.gnome.Gtranslator"&gt;current interface&lt;/a&gt; and propose some ideas in the &lt;a href="https://gitlab.gnome.org/GNOME/gtranslator/-/issues/159"&gt;corresponding task&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Mon, 30 May 2022 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2022-05-30:/outreachy-2022.html</guid><category>blog</category><category>gnome</category><category>outreachy</category><category>gtranslator</category><category>gtk4</category></item><item><title>Twitch: GNOME live coding streaming</title><link>https://danigm.net/twitch.html</link><description>&lt;p&gt;This year I've started with something that I wanted to do since the first COVID
lockdown, but never did. So as a first year resolution I decided to start
streaming and I created my Twitch channel &lt;a href="https://www.twitch.tv/abentogil"&gt;Abentogil&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/twitch-loop.png" /&gt;
&lt;/p&gt;

&lt;p&gt;I've been thinking about streaming since lockdown, when a group of Spanish
people created a nice initiative to teach kids how to code and other tech
stuff. I never participated on that initiative, but it was the seed of this.&lt;/p&gt;
&lt;p&gt;This year I've seen other GNOME streamers doing live coding, like &lt;a href="https://twitch.tv/ebassi"&gt;ebassi&lt;/a&gt;
and &lt;a href="https://www.youtube.com/c/GeorgesStavracas"&gt;Georges&lt;/a&gt; and, at the end, I pluck up the courage to start.&lt;/p&gt;
&lt;h2&gt;The plan&lt;/h2&gt;
&lt;p&gt;I've started with one our at the end of my day, so I'm trying to stream from
Monday to Thursday from 20:00 to 21:00 (CET).&lt;/p&gt;
&lt;p&gt;This routing also will help me to spend more time in free software development
regularly, so not just on weekends or holidays, but a bit of work everyday.&lt;/p&gt;
&lt;p&gt;Right now I've started working on my own projects, like &lt;a href="https://gitlab.gnome.org/danigm/timetrack/"&gt;Timetrack&lt;/a&gt; and
&lt;a href="https://gitlab.gnome.org/danigm/loop/"&gt;Loop&lt;/a&gt;. I've fixed some issues that I had with Gtk4 in Timetrack and I've
started to work on the Loop app to add a MIDI keyboard.&lt;/p&gt;
&lt;p&gt;The other day I discovered the &lt;a href="https://gitlab.gnome.org/bertob/app-ideas/-/issues/58"&gt;Music Grid&lt;/a&gt; app idea in the gitlab and it's
related with the MIDI implementation I've been working on, so the next thing
that I'll do on my streams is to create this music grid widget and add it to
the Loop app, to have a nice Music creation App.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/music-grid.png" /&gt;
&lt;/p&gt;

&lt;h2&gt;The language: Live coding in Spanish&lt;/h2&gt;
&lt;p&gt;I've decided to do the streaming mainly in Spanish. The main reason to do that
is because now you can find documentation and a lot of videos in English, but
for people that doesn't master the English language, it's harder to follow this
content and even harder to participate, to ask or say something.&lt;/p&gt;
&lt;p&gt;Spanish is also my main language, and the idea is not just create tutorials or
something like that, this is just for fun, and if I'm able to create a small
community or influence someone, I want to show that the language is not a
unbreakable barrier.&lt;/p&gt;
&lt;p&gt;I want to make this for the Spanish community, but that doesn't mean that for
some streams I cannot talk in English, all depends on the audience, of course,
if a non Spanish speaker comes and ask something, I'll answer in English and
try to do multilingual stream.&lt;/p&gt;
&lt;p&gt;The International English is the language to use in Free Software, but it's
really important to do not convert that in a barrier, because there are a lot
of different communities and people that don't know the language and the first
contact with something new is always better in your main language. Everything
is easier with help from people that's near to your cultural background.&lt;/p&gt;
&lt;h2&gt;The future&lt;/h2&gt;
&lt;p&gt;Right now I'm working on Loop and every stream is similar, live coding on this
Gtk4 music APP, but in the future I'll do different streams, doing different
things on each week day. These are the current ideas that could be a day on my
stream:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maintenance day: Work on my apps, code review, fixes, releases, and
   development.&lt;/li&gt;
&lt;li&gt;GNOME Love day: Give some love to the GNOME project, look for simple bugs to
   solve in different projects, help with translations, initiatives, and other
   possible one hour task to give some love to the project in general.&lt;/li&gt;
&lt;li&gt;Learning day: Pick some technology and learn by doing, for example, I
   can explore new programming languages writing a simple app, learn about
   Linux writing a driver, learn how to animate with blender, anything new to
   play and discover.&lt;/li&gt;
&lt;li&gt;Newcomers day: Create simple tutorial or introduction to some technology,
   How to write a GNOME Shell extension, how to create a Gtk4 app in python,
   teach to code from zero, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is just a list of ideas, if I discover any other interesting use of this
time and stream, I can do that. The final goal of this is to have fun, and if
someone learns something in the journey, that's even better.&lt;/p&gt;
&lt;p&gt;I'll also upload any interesting stream to &lt;a href="https://www.youtube.com/danigmx"&gt;youtube&lt;/a&gt;, so the good content
could be viewed on demand.&lt;/p&gt;
&lt;p&gt;So if you like this, don't hesitate to view some of the streams and say "Hola":&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="https://twitch.tv/abentogil"&gt;
        &lt;img src="/pictures/twitch.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 22 Jan 2022 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2022-01-22:/twitch.html</guid><category>blog</category><category>gnome</category><category>software</category><category>outreach</category><category>twitch</category><category>live coding</category></item><item><title>Loop: A simple music application</title><link>https://danigm.net/loop.html</link><description>&lt;p&gt;In the last year I've seen some &lt;a href="https://www.twitch.tv/leonbratt"&gt;really good musician&lt;/a&gt; that performs all
the instruments in a song with just a loop machine, recording each instrument
one by one in tracks and looping.&lt;/p&gt;
&lt;p&gt;I was thinking that it should be easy to have a desktop application that does
exactly the same, just some tracks to record some sounds and the playback with
a loop option, and that's what I created during this week.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/loop.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://gitlab.gnome.org/danigm/loop/"&gt;Loop application&lt;/a&gt; is just a simple Gtk4 application that uses gstreamer
to record tracks and then you can play each one at the same with or without
loop option. With that, a good musician could create the base melody of the
song and then sign on top of that. Unfortunately, I'm not a good musician, but
I can use this to play around.&lt;/p&gt;
&lt;p&gt;I've just created the &lt;a href="https://github.com/flathub/flathub/pull/2674"&gt;request on flathub&lt;/a&gt; to add the application, so if
everything is okay it will be available there soon, so more people can play
with this awesome toy.&lt;/p&gt;
&lt;p&gt;Right now it has the basic record, play and loop functionality with just four
tracks, so don't expect to have a professional music app (yet). The recording
and play times are not perfect and there's a delay, that's a known issue, but
I'm planning to fix this issue and add more functionality in the future, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make number of tracks configurable&lt;/li&gt;
&lt;li&gt;Import track from files&lt;/li&gt;
&lt;li&gt;Add a trim slider per track, to be able to adjust the recorder track to loop&lt;/li&gt;
&lt;li&gt;Metronome tick and clock to have a visual reference for recording tracks&lt;/li&gt;
&lt;li&gt;A record button, but export the combination of all tracks and mic to a mp3
   file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that's all. The logo and design is an initial version done by myself, so if
any designer wants to take a look, all contributions are welcome. And of course
any code contribution is also welcome.&lt;/p&gt;
&lt;p&gt;If you use this application and do some good or fun performance, please, ping
me on social networks and let me know.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 11 Dec 2021 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2021-12-11:/loop.html</guid><category>blog</category><category>gnome</category><category>software</category><category>music</category><category>loop</category></item><item><title>Endless Orange Week: Hack content creators platform (2)</title><link>https://danigm.net/hack-content-creators-1.html</link><description>&lt;p&gt;The past Friday was the last day of the &lt;a href="http://danigm.net/hack-content-creators.html"&gt;Endless Orange Week&lt;/a&gt;. It was a nice
and fun experience, and even if I was not able to do as much as I wanted, we
were able to make something that "works" in the Hack project.&lt;/p&gt;
&lt;h2&gt;The Hack Quest editor&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/clubhouse-custom-quests-1.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The first step to have custom quests on the Hack app was to complete the
&lt;a href="https://www.inklestudios.com/ink/"&gt;Ink language&lt;/a&gt; support. We started to work on this some time ago, but never
completed the functionality.&lt;/p&gt;
&lt;p&gt;I worked on that the first three days, updating the ink library and building
the missing pieces to be able to load quests from random paths. I've
implemented that in a way that the Hack application is able to receive a path
to a &lt;code&gt;.ink&lt;/code&gt; file, and it's able to build and run the quets.&lt;/p&gt;
&lt;p&gt;The Quests are not just the script, but they have some metadata, like title,
subtitle, description, difficulty and the card image to show on the interface.
To solve that I defined a "custom quest bundle format", that's bassically a
folder with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;questId&lt;/li&gt;
&lt;li&gt;quest.jpg&lt;/li&gt;
&lt;li&gt;metadata.json&lt;/li&gt;
&lt;li&gt;quest.ink&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I also added the functionality to import a bundle zip file and export with
the quest information.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/clubhouse-custom-quests-2.png" /&gt;
&lt;/p&gt;

&lt;p&gt;I created some command line options to use this new functionality:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ink&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FULL_INK_FILE_PATH&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;import&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;quest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PATH_TO_BUNDLE_OR_INK_FILE&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;Import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;quest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CUSTOM_QUEST_ID&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Quest creation interface and the Inky Editor&lt;/h3&gt;
&lt;p&gt;The first idea was to try to provide a full quest creation experience in the
app, but that was too much, so we decided to simplify the way to create quests
and depend on the &lt;a href="https://flathub.org/apps/details/com.inklestudios.Inky"&gt;Inky editor&lt;/a&gt; external tool. Manuel Quiñones took some time
to update the flatpak application with the latests ink version, so we can use
to create custom quests.&lt;/p&gt;
&lt;p&gt;The Inky editor provides help about the language, syntax highlighting and a
simple way to test the script, so it's a nice tool. The main problem with this
tool is that it doesn't provide a way to launch it with a file path so it's
not possible to integrate with the Hack app.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/clubhouse-custom-quests-3.png" /&gt;
&lt;/p&gt;

&lt;p&gt;So at the end, the Quest creation dialog is just a way to define the metadata
and to select the Quest ink files from your filesystem. How the ink script is
created is a decision to make for the content creator.&lt;/p&gt;
&lt;h3&gt;The future&lt;/h3&gt;
&lt;p&gt;We've no time to complete all we wanted to do, and I didn't create a new
release, so this new functionality is still not there. But we'll try to do a
release soon.&lt;/p&gt;
&lt;p&gt;Simon is working on some interface improvements and also on a new tutorial
Quest, so we can introduce the Custom Quest creation tool in the same app.&lt;/p&gt;
&lt;h2&gt;The Character editor&lt;/h2&gt;
&lt;p&gt;The other part of this week planning was the character editor. Joana did a
really nice work designing the application, the initial assets and the user
experience, but I had not too much time to work on the implementation.  So I
spent just one day working on this.&lt;/p&gt;
&lt;p&gt;The main idea was to create a new independent app, and then provide a way to
integrate with the Hack application and the custom Quest creation dialog. And
it'll be a simple application so maybe it could be useful or interesting for
other people, it's a fun way to play around and create random faces.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/avatar-creator-1.png" /&gt;
&lt;/p&gt;

&lt;p&gt;We just created the application &lt;a href="https://github.com/endlessm/avatar-creator/"&gt;Avatar Creator&lt;/a&gt;. I created a simple python
Gtk4 application and worked a bit on the basic functionality. So right now it
loads a list of svg assets and provide the 3x3 grid. You can click on a grid
cell and then choose what basic image should go there.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/avatar-creator-2.png" /&gt;
&lt;/p&gt;

&lt;p&gt;I added the initial set of basic images, created by Joana, to create this funny
robot faces, but the format is simple enough to extend with different "avatar
libraries" in the future.&lt;/p&gt;
&lt;p&gt;Right now it's also possible to export to png, so the app is functional, but it
needs a bit more work.&lt;/p&gt;
&lt;p&gt;My idea is to work a bit more in the following weeks, when I have some time, on
weekends or holidays and at some point, publish it in flathub. And lets see if
there are more developers interested on this app so it can grow.&lt;/p&gt;
&lt;p&gt;The application is simple enough to be a good place for GNOME newcomers and it's
also a fun project to work on. A simple toy app to create faces that could
have some potential, some future ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"Smart" random faces generator&lt;/li&gt;
&lt;li&gt;Configurable grid: Maybe is interesting to make it bigger or smaller to play
   around&lt;/li&gt;
&lt;li&gt;Programmed simple base image manipulations, like rotation, mirror, color&lt;/li&gt;
&lt;li&gt;Animation creation, maybe be able to export to gif&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Endless Orange Week experience&lt;/h2&gt;
&lt;p&gt;This week was a really nice experience, because we were working in a "personal"
chosen project, that we liked and without the day to day meetings, times
schedules and other related work stuff.&lt;/p&gt;
&lt;p&gt;But that was not all. In Endless we've different teams that work mostly
isolated, because we're working on different fields, we've some overlapping,
but we work day to day as small teams, and this week we were all using the same
slack channel to show our progress, and it was nice.&lt;/p&gt;
&lt;p&gt;Maybe now that we're not a big organization with a lot of workers, we can do
something like this more often, it's always good to know more about other
coworkers and to learn something that maybe it's not related with your main
project, but it could be interesting.&lt;/p&gt;
&lt;p&gt;I'm really happy that we did this Endless Orange Week, it's sad that it ended
too soon, I'm waiting to learn from my coworkers what amazing things they do
during this week and I'm looking forward the next year Orange Week!&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Mon, 15 Nov 2021 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2021-11-15:/hack-content-creators-1.html</guid><category>blog</category><category>hack</category><category>endless</category><category>orangeweek</category><category>work</category><category>gnome</category><category>software</category></item><item><title>Endless Orange Week: Hack content creators platform</title><link>https://danigm.net/hack-content-creators.html</link><description>&lt;p&gt;This week (Nov 8 – 12) I am participating in Endless Orange Week, a program
where the entire &lt;a href="https://www.endlessos.org/"&gt;Endless&lt;/a&gt; team engages in projects designed to grow our
collective learning related to our skills, work and mission.&lt;/p&gt;
&lt;p&gt;We propose a project, that could be anything, and then work during the whole
week, without distraction. I've choosed to work on the &lt;a href="https://www.hack-computer.com/"&gt;Hack project&lt;/a&gt;, that's a
really nice project that needs some love, because since the past year, we have
&lt;a href="https://www.endlessos.org/key"&gt;other priorities&lt;/a&gt;, so there's no time to improve the Hack app.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/clubhouse.png"&gt;
        &lt;img src="/pictures/clubhouse.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;The project: Hack content creators platform&lt;/h2&gt;
&lt;p&gt;The Hack application is a "Quest" launcher, and each Quest is an interactive
learning experience, a guided introduction to some technology or topic.&lt;/p&gt;
&lt;p&gt;Quests are just python scripts, with a simple library to expose messages to the
user, ask questions, or wait for desktop events, like launch an application,
focus an application, etc. And all these Quests are inside the application, and
are created by the Hack team and released with a new Hack app flatpak.&lt;/p&gt;
&lt;p&gt;The main idea of the project is to provide a simple Quest editor to allow any
Hack user to create and share their our Quests.&lt;/p&gt;
&lt;p&gt;To have this Hack content creators platform we'll need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To simplify the way we create Quests, instead of a python script, we'll uses
   a Domain Specific Language, called &lt;a href="https://www.inklestudios.com/ink/"&gt;Ink&lt;/a&gt;. We started to work on this, but
   we never ended the support.&lt;/li&gt;
&lt;li&gt;To create the interface to be able to import and export custom Quests, that
   could be zip bundles, with the Ink script and some images.&lt;/li&gt;
&lt;li&gt;To create the interface to write the actual Quests and save or bundle.&lt;/li&gt;
&lt;li&gt;Create some introductory Quest to explain "how to create your own Quests!".&lt;/li&gt;
&lt;li&gt;Create an character editor, to be able to "design" new characters for Quests.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;The Quest editor&lt;/h3&gt;
&lt;p&gt;The Quests will be written using the Ink language, and there's &lt;a href="https://flathub.org/apps/details/com.inklestudios.Inky"&gt;something done before&lt;/a&gt;.
The first idea is to just provide a text editor and some helpful information
about the format, and maybe a button to validate. But if there's time we can
use something advanced or even integrate the &lt;a href="https://flathub.org/apps/details/com.inklestudios.Inky"&gt;Inky&lt;/a&gt; editor.&lt;/p&gt;
&lt;h3&gt;The Character editor&lt;/h3&gt;
&lt;p&gt;Each Quest has a main character, and we've five in the Hack app right now, but
it could be great to be able to define new ones for custom Quests. That's the
idea of this part of the project.&lt;/p&gt;
&lt;p&gt;The initial idea is to have a library of character parts to combine, and the
editor will allow the user to combine this parts and maybe change colors, to be
able to create unique characters for your Quests.&lt;/p&gt;
&lt;h3&gt;The Team&lt;/h3&gt;
&lt;p&gt;I'm not working alone in this "side" project during the Endless Orange Week,
Simon Schampijer and Joana Filizola will be working on this too, so this is a
big task but we've a great team. Let's see how far we are able to go during
just one week.&lt;/p&gt;
&lt;h3&gt;The impact&lt;/h3&gt;
&lt;p&gt;This is just a project to try to keep alive more time the Hack application,
without a lot of effort or a whole team behind it. We are not able to put more
content there periodically, so if there's a way to create new content easily
and (maybe in the future) a way to publish, it'll be possible to create a
community around the project.&lt;/p&gt;
&lt;p&gt;And we have also new possibilities, in the near future, we can add some Hack
content to the Endless Key, and using the Ink language, so this editor could
help to bring more content there easily.&lt;/p&gt;
&lt;p&gt;And the final piece, the Character editor, could be an independent application,
a nice simple application that could be used to create your character for your
profile photo, or to generate random character pics.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Mon, 08 Nov 2021 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2021-11-08:/hack-content-creators.html</guid><category>blog</category><category>hack</category><category>endless</category><category>orangeweek</category><category>work</category><category>gnome</category><category>software</category></item><item><title>Fractal: Refactoring and the review process</title><link>https://danigm.net/gsoc-2020-1.html</link><description>&lt;p&gt;In this year GSoC, &lt;a href="https://aledomu.github.io/gnome/refactoring-fractal-remove-backend-i/"&gt;Alejandro&lt;/a&gt; is working on Fractal, moving code from the
backend to the client, to try to simplify the code used to communicate with the
matrix.org server and maybe in the future we can replace &lt;code&gt;fractal-matrix-api&lt;/code&gt;
with the &lt;a href="https://matrix.org/sdks/#matrix-rust-sdk"&gt;matrix-rust-sdk&lt;/a&gt;. And then we'll have less code in our project to
maintain.&lt;/p&gt;
&lt;p&gt;This is a great work, something needed in a project with a technological debt
of several years. I created this project to learn Rust, and also I was learning
about the matrix protocol during the project build. And other contributors do
the same. So we've been building one thing on top another for a lot of years.&lt;/p&gt;
&lt;p&gt;In this kind of community driven projects it's the way to go. For some time
we've people interested and developers think about the design and start change
some parts or to write new functionality following a new design pattern. But
voluntary developers motivation change in time and they left the project and
the next one continues the work with a different vision.&lt;/p&gt;
&lt;p&gt;It's not something bad, it's the greatness of the open source. Different people
has different motivations to participate in a free software project, and every
contribution is welcome. I'm the maintainer of the project and I've spent a lot
of time building Fractal, but I don't have the same motivation now to work on
the project, to it's good to have other people working on it so it can continue
alive.&lt;/p&gt;
&lt;p&gt;Alejandro is doing a great work and he's not a 4 months contributor. He's
working on the backend refactoring for 2 years now, step by step and he has
plans for the future.&lt;/p&gt;
&lt;p&gt;Refactoring a big project is always hard, because there's a lot of code
movement and always there's the fear to regressions.&lt;/p&gt;
&lt;p&gt;Rust is a great language and it shines when big code refactoring comes to the
scene. If it compiles, you &lt;strong&gt;know&lt;/strong&gt; that there's no memory errors, dangling
pointers and that kind of problems. If it builds it will work.&lt;/p&gt;
&lt;p&gt;But maybe it will work &lt;em&gt;different&lt;/em&gt;, so the review process is needed to ensure
that the application continues working.&lt;/p&gt;
&lt;p&gt;Automated tests are really useful for big code changes and project refactoring,
because you have a quick picture and some certainty that the project is
working. But we don't have tests in fractal :D, so someone should do that.&lt;/p&gt;
&lt;p&gt;So here I am. &lt;a href="https://gitlab.gnome.org/GNOME/fractal/-/merge_requests/581"&gt;Reviewing large MR&lt;/a&gt;, with a lot of lines. At least gitlab
makes this process a bit easier.&lt;/p&gt;
&lt;p&gt;What I'm trying to do in the review process is to just read the whole diff and
check if there's some problem in the code. And after every change, I run the
app and I do some tests, trying to use the functionality that could be broken
by those new changes.&lt;/p&gt;
&lt;p&gt;This takes a lot of time and it's not something fun to do... But someone has to
do that. And during that process, sometimes I learn something new. Reading code
is an interesting task and try to find bugs in code while reading it, is
something useful. To think about that code, what it does and why.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Tue, 30 Jun 2020 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2020-06-30:/gsoc-2020-1.html</guid><category>blog</category><category>gnome</category><category>software</category><category>fractal</category><category>gsoc</category></item><item><title>Fractal: Google Summer of Code 2020</title><link>https://danigm.net/gsoc-2020.html</link><description>&lt;p&gt;I'm glad to say that I'll participate again in the &lt;a href="https://summerofcode.withgoogle.com/"&gt;GSoC&lt;/a&gt;, as mentor. This
summer we'll try to implement multi-account support in &lt;a href="https://gitlab.gnome.org/GNOME/fractal"&gt;Fractal&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/fractal-gsoc.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The selected student is &lt;a href="https://summerofcode.withgoogle.com/projects/#6726209787920384"&gt;Alejandro Dominguez&lt;/a&gt; (aledomu), that is
collaborating with Fractal since the Seville Hackfest in December 2018, doing
a great work in the backend. Alejandro is young developer with a lot of energy
and ideas, so it's great to have this kind of people working on GNOME.&lt;/p&gt;
&lt;p&gt;I've not spend much time lately developing Fractal, the time and energy is
limited, but I'll try to use this GSoC mentorship to go back to the &lt;strong&gt;active&lt;/strong&gt;
Fractal development. My objective during this summer will be to stabilize,
fix bugs and improve the performance and create a new release at the end of the
GSoC, because we've a lot of new functionality in master, but I didn't spend
the time to do the release.&lt;/p&gt;
&lt;p&gt;The google summer of code is a great opportunity for free software, it give
us a full time working student, during three months, working on free software,
so it's really appreciated in a community where a lot of work is volunteer work.&lt;/p&gt;
&lt;p&gt;There are a total of 14 projects selected for this GSoC round for gnome, you
can take a look to the full list in the &lt;a href="https://summerofcode.withgoogle.com/organizations/5428225724907520/#projects"&gt;GSoC page&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 08 May 2020 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2020-05-08:/gsoc-2020.html</guid><category>blog</category><category>gnome</category><category>software</category><category>fractal</category><category>gsoc</category></item><item><title>End of GNOME Outreachy 2019</title><link>https://danigm.net/outreachy-2019-2.html</link><description>&lt;h2&gt;The Outreachy Program&lt;/h2&gt;
&lt;p&gt;The outreachy program ended the past week and we've done great improvements
during this four months of work. I'm very happy with the result and with the
work of the two interns and also the GNOME co-mentors that make this possible.&lt;/p&gt;
&lt;p&gt;If we're lucky the interns will continue contributing in the future and we can
see the GNOME comunity growing in developers and diversity 🎉.&lt;/p&gt;
&lt;h2&gt;GNOME translation editor (Gtranslator)&lt;/h2&gt;
&lt;video controls width="100%"&gt;
    &lt;source src="/pictures/gtranslator.webm" type="video/webm" /&gt;
    Video showing the new gtranslator search bar
&lt;/video&gt;

&lt;p&gt;&lt;a href="https://priyankasaggu11929.github.io/outreachy.html"&gt;Priyanka Saggu&lt;/a&gt; has been working on the new gtranslator search bar. It's a
replacement for the old search dialog with a new and modern search bar, inspired
in the gnome-builder search.&lt;/p&gt;
&lt;p&gt;This is how it looks in the current gtranslator version and the video is from
gtranslator master, that I'll try to release as 3.36.0 this weekend.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/gtranslator-old-search.png"&gt;
        &lt;img src="/pictures/gtranslator-old-search.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I want to thank to the other co-mentor of this project, Daniel Mustieles, who
has been testing and reviewing this new functionality.&lt;/p&gt;
&lt;h2&gt;Fractal&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://pitag.home.blog/"&gt;Sonja Heinze&lt;/a&gt; has been working on the video player for Fractal, so now
we can see videos inside the fractal message history instead of open it with
an external video player.&lt;/p&gt;
&lt;video controls width="100%"&gt;
    &lt;source src="/pictures/fractal.webm" type="video/webm" /&gt;
    Video showing the new fractal video player
&lt;/video&gt;

&lt;p&gt;This is now in master and will appear in the next release, that I'll try to
publish soon, maybe during this month. I want to fix some performance issues
first.&lt;/p&gt;
&lt;p&gt;Jordan Petridis (alatiera) has done a great work as co-mentor, guiding the
project and helping with the gstreamer.&lt;/p&gt;
&lt;p&gt;I've not presented any project proposal for the next outreachy, I want to take
a break and rest a bit before the Google Summer of Code, when I'll try to get
the multi-account support implemented in Fractal.&lt;/p&gt;
&lt;p&gt;This programs give me a bit of work, reviewing and guiding the intern, but it's
really great to have paid people working on free software, so I'm very happy to
be able to be a mentor in GNOME to help to boost some free software projects
using these resources.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 13 Mar 2020 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2020-03-13:/outreachy-2019-2.html</guid><category>blog</category><category>gnome</category><category>outreachy</category></item><item><title>GNOME Outreachy 2019</title><link>https://danigm.net/outreachy-2019.html</link><description>&lt;h2&gt;The Outreachy Program&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://www.outreachy.org/"&gt;Outreachy&lt;/a&gt; program provides internship to work in Free and Open Source
Software. This year I've proposed two projects as part of the GNOME project and
we've two interns working for three months, so we'll have a lot of improvements
in the following months!&lt;/p&gt;
&lt;p&gt;I'll be mentoring these interns, so I will need to spend some time helping them
to work on the existing codebase, but it worth it, if this makes more people to
collaborate in free software development and if this help us to improve some
useful apps.&lt;/p&gt;
&lt;p&gt;These two projects are Fractal and the GNOME translation editor. You can take a
look to the list of &lt;a href="https://www.outreachy.org/alums/"&gt;outreachy interns&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Fractal&lt;/h2&gt;
&lt;p&gt;Fractal is a Matrix.org gtk client, and I've proposed for this year program to
&lt;a href="https://www.outreachy.org/december-2019-to-march-2020-internship-round/communities/gnome/#fractal-implement-video-player-in-message-list"&gt;implement a video player in the message list&lt;/a&gt;. We've a preview for images,
for audio files but nothing for video files.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://pitag.home.blog/"&gt;Sonja Heinze&lt;/a&gt; is the one that will be working on this during the next three
months. She has been working during the past month in some small issues in
Fractal so I'm really sure that she will be able to do great contributions to
the project.&lt;/p&gt;
&lt;p&gt;Jordan Petridis (alatiera) will be helping in this project as a co-mentor, I
don't know a lot about gstreamer, so he'll be really helpful here with the
gstreamer and rust.&lt;/p&gt;
&lt;h2&gt;GNOME translation editor (Gtranslator)&lt;/h2&gt;
&lt;p&gt;GNOME translation editor (gtranslator) is a simple .po editor. I've proposed to
&lt;a href="https://www.outreachy.org/december-2019-to-march-2020-internship-round/communities/gnome/#gtranslator-rework-the-search-and-replace-dialog"&gt;Rework the search and replace dialog&lt;/a&gt;. We've right now a simple
find/replace modal dialog and I want to modernize the interface to integrate
better in the window as a popover.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://priyankasaggu11929.github.io/outreachy.html"&gt;Priyanka Saggu&lt;/a&gt; is the one that will be working on this during the next
three months. She has been working on gtranslator during the past month and she
has done great contributions and improvements during this time.&lt;/p&gt;
&lt;p&gt;Daniel Mustieles is the other co-mentor for this project. He's an experienced
GNOME translator so he will help us a lot with the app user experience design
and testing.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Mon, 16 Dec 2019 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2019-12-16:/outreachy-2019.html</guid><category>blog</category><category>gnome</category><category>outreachy</category></item><item><title>LAS 2019, Barcelona</title><link>https://danigm.net/las-2019.html</link><description>&lt;h2&gt;The event&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://linuxappsummit.org/"&gt;The Linux App Summit&lt;/a&gt; (LAS) is a great event that bring together a lot of
linux application developers, from the bigger communities, it's organized by
GNOME and KDE in collaboration and it's a good place to talk about the Linux
desktop, application distribution and development.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/las-2019.jpg"&gt;
        &lt;img src="/pictures/las-2019.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;This year the event was organized in Barcelona, this is not too far from my
home town, Málaga, so I want to be there.&lt;/p&gt;
&lt;p&gt;I sent a talk proposal and was accepted, so I was talking about distributing
services with flatpak and problems related to service deployment in a flatpaked
world.&lt;/p&gt;
&lt;p&gt;Clicking in this image you can find my talk in the event streaming. The sound
is not too good and my accent doesn't help, but there it is :D&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a target="_blank" href="https://youtu.be/4RE8gq0jLhA?t=4770"&gt;
        &lt;img src="/pictures/las-2019-talk.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;The event was a really great event, with really good talks, about different
topics, we've some technical talks, some talks about design, talks about
language, about distribution, about the market and economics, and at least two
about "removing" the system tray 😋&lt;/p&gt;
&lt;p&gt;It was really interesting the talk about the &lt;a href="https://conf.linuxappsummit.org/en/LAS2019/public/events/59"&gt;"future" inclusion of payments&lt;/a&gt; in
flathub because I think that this will give a new incentive to people to write
and publish apps in flathub and could be a great step to get donations for
developers.&lt;/p&gt;
&lt;p&gt;Another talk that I liked was the one about the &lt;a href="https://conf.linuxappsummit.org/en/LAS2019/public/events/15"&gt;maintenance of flatpak repositories&lt;/a&gt;,
it's always interesting to know how the things works and this talk give an easy
introduction to ostree, flatpak, repositories and application distribution.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/las-2019-group.jpg"&gt;
        &lt;img src="/pictures/las-2019-group.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Besides the talks, this event is really interesting for the people that bring
together. I've been talking with a lot of people, not too much, because I'm a
shy person, but I've the opportunity to talk a bit with some Fractal
developers, and during a coffee talk with Jordan Petridis, we've time to share
some ideas about a cool new functionality that maybe we can implement in the
near future, thanks to the outreachy program and maybe some help from the
gstreamer people.&lt;/p&gt;
&lt;p&gt;I'm also very happy to be able to spend some time talking with Martín Abente,
about sugar labs, the hack computer and the different ways to teach kids with
free software. Martín is a really nice person and I liked a lot to meet him and
share some thoughts.&lt;/p&gt;
&lt;h2&gt;The city&lt;/h2&gt;
&lt;p&gt;This is not my first time in Barcelona, I was here at the beginning of this
year, but this is a great city and I've no time to visit all the places the
first time.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/las-2019-barcelona.jpg"&gt;
        &lt;img src="/pictures/las-2019-barcelona.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;So I've spent the Thursday afternoon doing some tourism, visiting the "Sagrada
Familia" and the "Montjuïc" fountain.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/las-2019-sagrada.jpg"&gt;
        &lt;img src="/pictures/las-2019-sagrada.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;If you have not been in Barcelona and you have the opportunity to come here,
don't hesitate, it's a really good city, with a great architecture to admire
and really nice culture and people, and here you can find good food to enjoy.&lt;/p&gt;
&lt;h2&gt;Thank you all&lt;/h2&gt;
&lt;p&gt;I was sponsored by the &lt;a href="https://www.gnome.org/foundation/"&gt;GNOME Foundation&lt;/a&gt;, I'm really thankful for this
opportunity, to come here, give a talk and share some time with great people
that makes the awesome Linux and open source community possible.&lt;/p&gt;
&lt;p style="text-align: center"&gt;
    &lt;a href="https://gnome.org"&gt;
        &lt;img src="/pictures/sponsored-by-foundation-round.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I want to thank to my employer &lt;a href="https://endlessos.com/es/"&gt;Endless&lt;/a&gt; because it's really a privilege to
have a job that allows this kind of interactions with the community, and my
team &lt;a href="https://hack-computer.com/"&gt;Hack&lt;/a&gt;, because they I've missed some meetings this week and I was not
very responsive during the week.&lt;/p&gt;
&lt;p&gt;And I want to thank to the LAS organization, because this was a really good
event, good job, you can be very proud.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Thu, 14 Nov 2019 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2019-11-14:/las-2019.html</guid><category>blog</category><category>gnome</category><category>LAS</category><category>endless</category></item><item><title>Andaluh-rs, a lib to transcript Spanish to Andaluh</title><link>https://danigm.net/andaluh.html</link><description>&lt;p&gt;Spain is a big region with a lot of languages, we've the official one for
the whole region, Spanish or Castilian (es-ES), and other officials languages
spoken in other regions, like the Galician (gl-ES) spoken in Galicia,
the Basque (eu-ES), spoken in the Basque Country, the Catalan (ca-ES)
spoken in Catalonia.&lt;/p&gt;
&lt;p&gt;But we've more languages that doesn't have the same official support, the
Valencian, the Aragonese, the Asturian, &lt;a href="https://en.wikipedia.org/wiki/Languages_of_Spain"&gt;and more&lt;/a&gt;. And also we've some
&lt;strong&gt;dialects&lt;/strong&gt; like the &lt;em&gt;Andalusian&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;All of this languages was discredit during the dictatorship period when the
only official language was the Spanish and the others was treated like
vulgar languages or non educated one, and in some cases the language was
&lt;em&gt;prohibited&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Whe the &lt;em&gt;democracy&lt;/em&gt; arrives, some regions spend a lot of resources trying to
recover the language and are supporting it until now with official institutions
and lessons in the official education system. But in other regions the stigma
continued until today.&lt;/p&gt;
&lt;p&gt;That's the case of the &lt;em&gt;Andalusian&lt;/em&gt;, that right now is not considered a language
but a dialect. In any case, this &lt;em&gt;dialect&lt;/em&gt; is treated as a non cultivated
language, spoken by illiterates. We've a lot of Spanish movies and Series where
the character that spoke &lt;em&gt;Andalusian&lt;/em&gt; was the illiterate, from the countryside
or the servant of the family.&lt;/p&gt;
&lt;h2&gt;Be proud of it&lt;/h2&gt;
&lt;p&gt;In Andalusia we've a lot of culture, literature and music done in &lt;em&gt;Andalusian&lt;/em&gt;,
but we don't have a way to write that and many people try to avoid the &lt;em&gt;acent&lt;/em&gt;
and the local words to look more cultured and because there's no a writing
system, we write in Spanish, it's hard to write lyrics, poetry and other
kind of literature. The Andalusian is not only a way to talk, some words are
short and we've contractions and other vowel sounds so if you write in Spanish,
it's not the same as spoken, some information is missed and for example in music
or poetry the metric doesn't match.&lt;/p&gt;
&lt;p&gt;There's a &lt;a href="https://andaluh.es/"&gt;movement&lt;/a&gt; trying to define a writing &lt;strong&gt;Andalusian&lt;/strong&gt; and promoting the
language, trying to make people proud of it and talk and write without complexes.&lt;/p&gt;
&lt;h2&gt;Translator, here comes the code&lt;/h2&gt;
&lt;p&gt;And there's a &lt;a href="https://github.com/andalugeeks/"&gt;group of developers&lt;/a&gt; that are working in some tools to provide
direct translation from Spanish and other tools to ease the &lt;strong&gt;Andalusian&lt;/strong&gt; writing.&lt;/p&gt;
&lt;p&gt;I like to write code and I'm always happy to find new problems to solve, to learn
new languages, tools and to spend some time trying to code something that I've not
done before. So I decided to write a translator from Spanish to Andaluh using rust,
and I've created the &lt;a href="https://github.com/andalugeeks/andaluh-rs"&gt;andaluh-rs&lt;/a&gt; lib.&lt;/p&gt;
&lt;p&gt;The translator is more or less easy, there're some rules that should be applied
from top to bottom that basically replaces some group of letters. There's a
implementation in &lt;a href="https://github.com/andalugeeks/andaluh-py"&gt;python&lt;/a&gt; that uses regular expressions for that. There're
a lot of regular expressions, so I thougth that it could be easy to use a parser,
so I used the &lt;a href="https://github.com/pest-parser/pest"&gt;pest parser&lt;/a&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// supress muted /h/&lt;/span&gt;

&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;h&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;initial_h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;CH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;inner_ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;inner_h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="n"&gt;inner_ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;hua&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ua&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;UA&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Ua&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;uA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;hue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ue&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;UE&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Ue&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;uE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;noh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="n"&gt;CH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;SOI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_h&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;hua&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inner_ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inner_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;noh&lt;/span&gt;&lt;span class="o"&gt;+&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;+&lt;/span&gt;&lt;span class="w"&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've defined each rule in the pest format, so I've a parser for each rule
and then I can replace the word with the correct replacement.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;h_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&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;defs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;H_RULES_EXCEPT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;initial_h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;inner_h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&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;as_str&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;keep_case&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;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;keep_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;güe&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&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;as_str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hua&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;keep_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;gua&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&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;as_str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&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;To simplify the code, I've defined the &lt;code&gt;rule&lt;/code&gt; macro, with the code used in
all rules:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="fm"&gt;macro_rules!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$rule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$($t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;pat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$($t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$r&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$rule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$($t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;pat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replace_exceptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AndaluhParser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&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="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&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;as_rule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="cp"&gt;$(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$($t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="cp"&gt;$r&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="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;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;as_str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;outstr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;outstr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replace_exceptions_back&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;outstr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;repl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outstr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&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 because the Spanish and the Andaluh languages uses unicode and rust Strings
can not be iterated by &lt;em&gt;unicode&lt;/em&gt;, I've used &lt;code&gt;unicode_segmentation&lt;/code&gt; crate, and
defined some utility macros to get the real String len and to get slices of that
String.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="fm"&gt;macro_rules!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;UnicodeSegmentation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;graphemes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="fm"&gt;macro_rules!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;::&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;$start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;::&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="fm"&gt;macro_rules!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&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;With all this done, we only have to apply all rules, in the correct order,
to the input string so we can get the translated String as output.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;epa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// TODO: escape links&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;h_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;x_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ch_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;gj_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;v_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ll_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;l_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;psico_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;vaf_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;word_ending_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;digraph_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;exception_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;word_interaction_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&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;output&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="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&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;Performance&lt;/h2&gt;
&lt;p&gt;This code is not the best one, I'm doing a lot of string operations with copies
and clones, I'm sure that anyone with more experience with rust can view a lot
of points where we can optimize this code. At first I thought that the translation
could be done during the parsing, keeping a length to be able to view backward
and forward.&lt;/p&gt;
&lt;p&gt;Maybe it's possible to read char by char, keeping a buffer, and detecting if
we can apply any of the rules with the content in the buffer, but I've based
this lib in the python one, so for me it was easier to translate each regex to
pest regex and then do the same translations in the lib.&lt;/p&gt;
&lt;p&gt;But I still think that there's a better solution for this problem, but some times
it's better to have something that just works instead of a never done best
solution.&lt;/p&gt;
&lt;p&gt;During this process I've learned to use pest and I've been playing a lot with
regular expressions, so it was a fun project.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 06 Jul 2019 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2019-07-06:/andaluh.html</guid><category>blog</category><category>gnome</category><category>software</category><category>andaluh</category><category>language</category></item><item><title>EndlessOS dual boot with Fedora</title><link>https://danigm.net/endlessos-dual-boot.html</link><description>&lt;p&gt;I've a &lt;em&gt;ThinkPad X1 Yoga&lt;/em&gt;, that's basically a &lt;em&gt;ThinkPad X1 Carbon 4th&lt;/em&gt; but
with a touch screen with a pencil that works like a wacom tablet.&lt;/p&gt;
&lt;p&gt;I've this laptop since 2016. The first thing that I did when I received it
was install a GNU/Linux operating system. I'm a GNU/Linux user since the
year 2000 going through a lot of distributions, Debian, Ubuntu, Archlinux, etc.&lt;/p&gt;
&lt;p&gt;When I received this computer I've a customized Archlinux there, and I wanted
to &lt;code&gt;dd&lt;/code&gt; my harddisk and boot, but I was unable to do that. I didn't know
nothing about UEFI and I was unable to boot the Archlinux installer.&lt;/p&gt;
&lt;p&gt;So I decided to go ahead and change my main distribution. I installed Fedora
and almost all the hardware worked so I keep that one and was happy, until
today :P&lt;/p&gt;
&lt;h2&gt;OStree, the new way of distribute GNU/Linux&lt;/h2&gt;
&lt;p&gt;With OStree and flatpak, there's a new way to distribute GNU/Linux, instead of
use directly a package manager and update each package, we can use OStree and
mount the root filesystem as read only and do full OS upgrades without broken
packages and dependencies and so. The operating system go as is and the user
should try hard to break it.&lt;/p&gt;
&lt;p&gt;The other great thing about OStree is that it's like a git repository, so you
can have different branches and a history, so you can easily go back and forward,
it's really easy to test the beta and go back without breaking your system.&lt;/p&gt;
&lt;p&gt;The main problem is that you &lt;em&gt;can't&lt;/em&gt; install anything on your OS, you should
use contained apps like flatpak or install by hand, you can't use &lt;code&gt;apt-get&lt;/code&gt;.
But that's okay for a day to day user, a power user always can &lt;code&gt;unlock&lt;/code&gt; the OS
and use it as a normal GNU/Linux distribution.&lt;/p&gt;
&lt;p&gt;I wanted to try one of these distributions. The logical choice was &lt;a href="https://silverblue.fedoraproject.org/"&gt;Silverblue&lt;/a&gt;
because it's a Fedora and I'm using it for three years now, but there's another
option, &lt;a href="https://endlessos.com/"&gt;EndlessOS&lt;/a&gt; is also OStree based, and &lt;a href="http://danigm.net/endless.html"&gt;I'm working with this OS&lt;/a&gt;,
so I should give a try and use EndlessOS.&lt;/p&gt;
&lt;h2&gt;The EndlessOS install process&lt;/h2&gt;
&lt;p&gt;Like all the new modern GNU/Linux distros, EOS comes with a easy to use installer,
you only need to boot from the USB and click next until it's done...&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/eos/endless-install-2.jpg"&gt;
        &lt;img src="/pictures/eos/endless-install-2.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;But here we've the first problem. I've more than one partition in my disk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/dev/sda1: UEFI&lt;/li&gt;
&lt;li&gt;/dev/sda3: Fedora /&lt;/li&gt;
&lt;li&gt;/dev/sda8: Swap&lt;/li&gt;
&lt;li&gt;/dev/sda4: Fedora /boot&lt;/li&gt;
&lt;li&gt;/dev/sda6: /home&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I want to keep my Fedora (who knows if something bad happens) and try to use the
same home partition for my new EOS. But the installer only give me the option
to erase all and have a clean system. But that won't stop me.&lt;/p&gt;
&lt;p&gt;Let's go back and instead of reformat I will click on &lt;em&gt;Try Endless OS&lt;/em&gt;:&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/eos/EndlessOS-Installation.png"&gt;
        &lt;img src="/pictures/eos/EndlessOS-Installation.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;What we need to do, Robert McQueen gave me some directions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The constraints for booting Endless are 1) you use our grub, and 2) the root
partition is labelled "ostree"&lt;/p&gt;
&lt;p&gt;So if you have an EFI system, you can copy our EFI binaries into the ESP, and
create a new partition for Endless, then dd the endless ostree filesystem into
it&lt;/p&gt;
&lt;p&gt;Then you should be able to boot, if you add a boot entry for the endless grub
to your firmware, or make it the default (by providing the fallback boot64.efi
or whatever it's called), or chain load it from another Linux loader&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Install EOS with other linux (EFI system)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Boot from USB, select try&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Launch the gnome-disk-utility and prepare a partition. I've not free space,
but I was able resize my Fedora partition and I split in two of the same size,
now I've a new ext4 partition &lt;code&gt;/dev/sda7&lt;/code&gt; with 25GB.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the endless ostree:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;su
&lt;span class="c1"&gt;# dd if=/dev/mapper/endless/image3 /dev/sda7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Copy endless grub to EFI. I mounted all partitions in /tmp, the first
partition in &lt;code&gt;/tmp/sda1&lt;/code&gt; and the EOS efi in the &lt;code&gt;/tmp/EOS&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# mkdir /tmp/sda1 /tmp/EOS
# mount /dev/sda1 /tmp/sda1
# mount /dev/mapper/endless-image1 /tmp/EOS
# cp -rf /tmp/EOS/EFI/endless/ /tmp/sda1/EFI
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Add the new boot entry:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# efibootmgr -c -d /dev/sda -p 1 -L EOS -l \\EFI\\endless\\grubx64.efi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Set as default boot:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# cp /tmp/EOS/EFI/endless/grubx64.efi /tmp/sda1/EFI/Boot/bootx64.efi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Reboot and create my default user. Then I add my home partition to the &lt;code&gt;/etc/fstab&lt;/code&gt;
file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;UUID=c885e171-1a03-4afb-8519-f9fe26fe92b7 /sysroot/home ext4 defaults 1 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And because the first user in EOS is the shared account, with UID 1000, I've to
change the UID of my user editing the file in &lt;code&gt;/etc/passwd&lt;/code&gt;. Then I rebooted
again and all works. I've all my flatpak apps installed in the user space working.&lt;/p&gt;
&lt;p&gt;So here we're, with a shiny new OS working like a charm:&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/eos/screenshot.png"&gt;
        &lt;img src="/pictures/eos/screenshot.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;The EFI and efibootmgr (who needs grub to select the OS?)&lt;/h2&gt;
&lt;p&gt;I didn't know much about UEFI and I was very impressed about how easy is to
update this from GNU/Linux. There's a tool called &lt;code&gt;efibootmgr&lt;/code&gt; that does all
the work, and you can mount the partition, that's a FAT32, and put files there.&lt;/p&gt;
&lt;p&gt;In my ThinkPad, I can boot directly to the UEFI boot menu pressing F12 during
the boot, and that menu can be changed using the &lt;code&gt;efibootmgr&lt;/code&gt; so it's not
needed anymore to use the grub2 OS selection interface, I can use the UEFI
menu for that!&lt;/p&gt;
&lt;p&gt;This has some disadvantages, if you remove files from the UEFI partition, you
can break the whole boot, so review all before any change.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 25 May 2019 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2019-05-25:/endlessos-dual-boot.html</guid><category>blog</category><category>gnome</category><category>work</category><category>endless</category><category>fedora</category></item><item><title>Kung-fu Master, Blindfolded debugging</title><link>https://danigm.net/blindfolded.html</link><description>&lt;p&gt;Recently I've been working in the &lt;a href="https://endlessos.com/"&gt;EOS&lt;/a&gt; update. The change is really big
because EOS has some modifications over the upstream code so there are a lot
of commits applied after the last stable upstream commit.&lt;/p&gt;
&lt;p&gt;EOS 3.5 was based on gnome 3.26 and we are updating to use the gnome 3.32 so
all the downstream changes done since the last release should be rebased on top
of the new code and during this process we refactor the code and commits using
new tools and remove what's now in gnome upstream.&lt;/p&gt;
&lt;p&gt;I've been working in the Hack computer custom functionality that's on top of
the EOS desktop and basically I've been rebasing the code in the shell to
do the Flip to Hack, the Clubhouse, a side component and notification override
to propse hack quests and the wobbly windows effects.&lt;/p&gt;
&lt;p&gt;I've been working mainly with gnome shell and updating javascript code to the
new gjs version, but this was a big change and some rare bugs.&lt;/p&gt;
&lt;p&gt;Here comes the blindfolded debugging. I've a development system with a lot of
changes and a functionality in the gnome shell that depends on different
projects, applications and technologies.&lt;/p&gt;
&lt;p&gt;I don't know the code a lot, because I'm not the one that wrote all of this,
I'm working here since February, so I know a bit how things works, but I'm not
the one who knows everything, there are a lot of rare cases that I don't know
about.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/blindfolded/kungfu.gif" /&gt;
&lt;/p&gt;

&lt;p&gt;I've found and fixes several bugs in different projects, that I don't know about
a lot, during this process. How can I do that? If you are a developer that
write code since a few years maybe you've experienced something similar, I'm
calling this the &lt;strong&gt;blindfolded debugging technique&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Start to change code without knowing exactly what you're doing, but with a
small feeling that maybe that line is the problem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This technique is only for experienced programmers, as a kungfu master that
put a blindfold in their eyes to fight against an opponent, the developer that
will be brave enough to try this should have a lot of experience or he will fail.&lt;/p&gt;
&lt;p&gt;You're an experienced developer, but you don't know the software that you're
debugging. It doesn't matter. The same way that in a kungfu fight you don't
know your opponent, but almost every fighter has two arms and two legs, so
more or less you'll know that he'll try to punch you or kick you, you've a
clue. As a programmer, every software has an structure, functions, loops...&lt;/p&gt;
&lt;p&gt;No matters who wrote that or how old that code is, if you're an experienced
programmer you'll feel the code and without knowing exactly why or how, you
will be able to look at one line and says: &lt;em&gt;Here you're little buggy friend&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Maybe I'm only trying to justify my lucky with a story to feel better and
say that I'm not a dumb changing random lines to try to find a bug, but I'm
sure that other developers has this feeling too, that feeling that guides you
to the exact problem but that you're not able to rationalize.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/blindfolded/fight.gif" /&gt;
&lt;/p&gt;

&lt;p&gt;I think that this is the experience, the expertise is your body and your
inner brain doing the work meanwhile you don't need to think about it or
know exactly what you are doing.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sun, 05 May 2019 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2019-05-05:/blindfolded.html</guid><category>blog</category><category>gnome</category><category>software</category><category>debugging</category></item><item><title>I'm a hacker</title><link>https://danigm.net/endless.html</link><description>&lt;h2&gt;I've the strength of the one that fails, trains and returns&lt;/h2&gt;
&lt;p&gt;Some time ago I wrote &lt;a href="http://danigm.net/gnome-gtk-developer.html"&gt;about my interview process&lt;/a&gt; trying to get a job at
GNOME. After this blog post was published in the GNOME planet, I received a lot
of comments from the &lt;em&gt;Great&lt;/em&gt; GNOME community.&lt;/p&gt;
&lt;p&gt;This guided me to take a look to the companies that was looking for GNOME
developers and after some interviews I finally get an offer to work on
&lt;a href="https://endlessos.com/"&gt;Endless&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All the process was online, I've been working from home since 2011 and I want
to continue like this. I'm living in Málaga, the south of Spain and I was
looking for a remote job.&lt;/p&gt;
&lt;p&gt;I've a very nice interview process, talking with developers that I knew about
from their contributions to free software and these interviews weren't
technical, that's a good thing about the free software contribution and
the community, if someone wants to know my technical skills, he only need to
look to my gitlab or github and he will find a lot of code. These interviews
were personal and with some management questions, to know if I'll fit in the
team and the company.&lt;/p&gt;
&lt;p&gt;I did the interview to work directly on the Endless OS, in the desktop team,
but after some interviews I ended in other team, working with the Endless OS
and all GNOME technology, but in the &lt;a href="https://hack-computer.com/"&gt;Hack Computer&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The hack computer&lt;/h2&gt;
&lt;p&gt;The hack computer is an educational project. The main idea is to provide a
fully functional computer for kids to be their first personal computer and also
provide a way to teach kids to &lt;em&gt;hack&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The computer is a usual laptop, with an Endless OS running, and with some extra
applications that try to guide kids to explore, modify and finally hack the
computer learning in the process about GNU/Linux, the code and that any part
of your software can be hacked to make it better, an introduction to the
free software and the great world of open source code that you can modify to
learn, play or simply because you can.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/SN7tC4XnGko" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;The way to guide kids to learn is like a video game, with characters that will
appears in your desktop and will guide you with tips and challenges and after
each quest completed the kid will learn something, playing with his computer.&lt;/p&gt;
&lt;p&gt;If you like this and want to collaborate, &lt;a href="https://jobs.lever.co/endless"&gt;we're hiring&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Endless OS&lt;/h2&gt;
&lt;p&gt;The hack computer is built on top of the Endless OS. Endless OS is based on
debian and the desktop is a modified gnome shell, but it's not the usual debian
derivative, it's based on &lt;a href="https://ostree.readthedocs.io/en/latest/"&gt;OSTree&lt;/a&gt;. The main difference is that the root
filesystem is read only and updates are managed with ostree, that's like a git
repository.&lt;/p&gt;
&lt;p&gt;This kind of Operating System is easier to maintain, because the user &lt;em&gt;can't&lt;/em&gt;
modify the base system, so this means that he was unable to &lt;em&gt;break it&lt;/em&gt;. All user
applications are installed via flatpak, so are &lt;em&gt;independent&lt;/em&gt; of the &lt;em&gt;OS&lt;/em&gt;
version and because of flathub you can install latests version of apps without
the need to update the full operating system.&lt;/p&gt;
&lt;p&gt;This is the way that &lt;a href="https://silverblue.fedoraproject.org/"&gt;Fedora SilverBlue&lt;/a&gt; is trying to follow and is a &lt;em&gt;new&lt;/em&gt;
way to build and distribute GNU/Linux.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/hack-mount-tree.png"&gt;
        &lt;img src="/pictures/hack-mount-tree.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;The end of an Era&lt;/h2&gt;
&lt;p&gt;I've been working in &lt;a href="https://wadobo.com"&gt;Wadobo&lt;/a&gt; since 2011. I created that company with two
college friends to try to continue hacking like we were hacking in the Seville
LUG &lt;a href="https://sugus.eii.us.es/"&gt;SUGUS&lt;/a&gt; and earn money from that, and we did it for a long time.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/wadobo-old-2.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;We've been working in free software projects and with free software technology,
contributing to the community as much as we can and always trying to free our
work.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/wadobo-old-1.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;We created some projects that grow up and follow its own path, like the
agora voting system, that Edulix (Eduardo Robles) converts in &lt;a href="https://nvotes.com/"&gt;nvotes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The self employment was a really good experience. I've been doing was I want
for a long time, taking time to contribute to GNOME and other projects when
I need it, because I was the one deciding what I want to do. But in the other
hand, I've been stuck in the local consultancy market for a long time.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/wadobo-old-3.jpg" /&gt;
&lt;/p&gt;

&lt;p&gt;I'm not a business man, I'm a developer and I'm based in Spain, where there
is no money inverted in innovation or we was unable to find that money, so we
were doing django web apps for a living and spending some time in our interests
in the extra time we've. So when I found the possibility to work full time in
an innovative project, I've no choice.&lt;/p&gt;
&lt;p&gt;We'll try to continue with the &lt;strong&gt;Wadobo&lt;/strong&gt; idea, like a group of people
interested in free software and new technologies, maybe this will derive into
a Linux User Group or maybe in the future someone takes the initiative and
build a business around this again.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/wadobo-old-4.jpg" /&gt;
&lt;/p&gt;

&lt;h2&gt;Hacking&lt;/h2&gt;
&lt;p&gt;I'm really happy with this change. Endless OS and the hack computer are really
great projects with a lot of bleeding edge technology and a really great group
of people, and I'm really exited to be able to work with people all around the
world in a project with a global vision.&lt;/p&gt;
&lt;p&gt;I don't want to leave the roots I've here with my Wadobo friends and the local
community and local technology ecosystem. This is also related with the Endless
vision. Spain, and here in the south, in Andalucía, we've a technological
breach that we try to fill with free software.&lt;/p&gt;
&lt;p&gt;I'll continue working with the Seville University, because they give me a lot
and I think it's a talent pool, we only need to guide those students to the
GNOME community or to other free software communities out there, to &lt;em&gt;save&lt;/em&gt;
their souls from the privative software and the dark side.&lt;/p&gt;
&lt;p&gt;I'll continue supporting initiatives like the &lt;a href="http://concursosoftwarelibre.org/"&gt;Free Software Contest&lt;/a&gt;&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;a href="http://concursosoftwarelibre.org/"&gt;
    &lt;img src="http://concursosoftwarelibre.org/1819/files/images/banners/cusl2_500x455.png" /&gt;
  &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;By the way, I'll be talking about rust in the Seville University the next
Tuesday 5th of March 2019, if you're in Seville, come to talk with me :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sun, 17 Feb 2019 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2019-02-17:/endless.html</guid><category>blog</category><category>gnome</category><category>work</category><category>endless</category><category>hack</category></item><item><title>Timetrack app for GNOME</title><link>https://danigm.net/timetrack.html</link><description>&lt;p&gt;This week I started a new small project called &lt;a href="https://flathub.org/apps/details/net.danigm.timetrack"&gt;Timetrack&lt;/a&gt;, you can find
the code in the &lt;a href="https://gitlab.gnome.org/danigm/timetrack"&gt;gnome gitlab&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/timetrack1.png"&gt;
        &lt;img src="/pictures/timetrack1.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;This is a simple app to store an activity log in a sqlite database and to
provide useful reports in the future. The main idea is to use this for may day
to day work, so I'll be able to keep track of my working hours and other
activities.&lt;/p&gt;
&lt;h3&gt;The need&lt;/h3&gt;
&lt;p&gt;There's a lot of timetrack applications. There are good web apps and also some
desktop applications. At first, I tried to find a gnome-shell plugin, but I
didn't find any extension that is good for me.&lt;/p&gt;
&lt;p&gt;I've had a &lt;a href="https://github.com/wadobo/django-objs"&gt;custom simple web app&lt;/a&gt; to track my time.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/wadobo-objs.png"&gt;
        &lt;img src="/pictures/wadobo-objs.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I've been using this app for a long time, but I don't like to use web apps for
simple tasks so I want something local.&lt;/p&gt;
&lt;p&gt;So I tried with some gtk desktop applications, but none of the applications
that I've found looks good to me.&lt;/p&gt;
&lt;p&gt;I wanted a simple timetrack app, without any complex stuff, so I thought that it
won't be hard to implement it. So I started with a simple python app based
on the code of &lt;a href="https://gitlab.gnome.org/World/PasswordSafe/"&gt;Password Safe&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/timetrack2.png"&gt;
        &lt;img src="/pictures/timetrack2.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;So I started to code and in two days I had a simple application that &lt;em&gt;works&lt;/em&gt;.
The applications is now on flathub so it's available for use.&lt;/p&gt;
&lt;h3&gt;The functionality&lt;/h3&gt;
&lt;p&gt;Right now, Timetrack has a limited set of features, but I think I'll improve
the app with new features as soon as I need something. So, the current version
can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Track activity time with a simple button, showing the activity time spent&lt;/li&gt;
&lt;li&gt;List last activities&lt;/li&gt;
&lt;li&gt;Edit / Delete activities&lt;/li&gt;
&lt;li&gt;Simple report by day, week or month&lt;/li&gt;
&lt;li&gt;Report navigation using a calendar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've plans to add more functionalities in the near future, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Report export to plain text and csv&lt;/li&gt;
&lt;li&gt;Comments for activities&lt;/li&gt;
&lt;li&gt;Tags for activities&lt;/li&gt;
&lt;li&gt;Detailed reports&lt;/li&gt;
&lt;li&gt;Activity Graphs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this is stored in a sqlite database, if you install the app using flatpak
you can find the database in this path:
&lt;code&gt;~/.var/app/net.danigm.timetrack/data/timetrack.sqlite3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I've &lt;em&gt;done&lt;/em&gt; the icon for this app using the gnome-clocks icon and adding some
colors to it.&lt;/p&gt;
&lt;p&gt;If you find this app useful, don't hesitate and use it. Any feature request or
bug report is welcome. And of course, all is free software, so if you want to
collaborate, go ahead and send me some Merge Request.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 26 Jan 2019 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2019-01-26:/timetrack.html</guid><category>blog</category><category>gnome</category><category>timetrack</category><category>python</category></item><item><title>GNOME Outreachy mentorship</title><link>https://danigm.net/gnome-outreachy-2018.html</link><description>&lt;p&gt;The &lt;a href="https://www.outreachy.org/"&gt;Outreachy&lt;/a&gt; program is a three month internship to work in FOSS. There
are two periods for the outreachy, the first one from December to March and
the other one from May to August. It's similar to the Google Summer Of Code,
but in this case the interns doesn't need to be students.&lt;/p&gt;
&lt;p&gt;I proposed some ideas for interts to work on GNOME, with me as a mentor. I
wrote three proposals this time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extend &lt;a href="https://www.outreachy.org/communities/cfp/gnome/project/extend-fractal-media-viewer-with-video-support-and/"&gt;Fractal&lt;/a&gt; media viewer with video support and explore video
   conference&lt;/li&gt;
&lt;li&gt;Create &lt;a href="https://www.outreachy.org/communities/cfp/gnome/project/create-gtranslator-initial-integration-with-damned/"&gt;Gtranslator&lt;/a&gt; initial integration with Damned Lies&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.outreachy.org/communities/cfp/gnome/project/books-improve-the-epub-support-in-gnome-books/"&gt;Books&lt;/a&gt; : Improve the epub support in gnome-books&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There was people interested in all of three projects, but for the Books app
we don't have any real contribution so there was no real applicants.&lt;/p&gt;
&lt;p&gt;I've two good proposals for Fractal and for Gtranslator, so I approve both and
the Outreachy people approve these two interts. So we get two new devs working
in GNOME for three months as interns.&lt;/p&gt;
&lt;p&gt;This is something great, paid developers working in my proposals is a good
thing, but this implies that I need to do the mentor work for these two interns
during the three months period, so it's more work for me :/&lt;/p&gt;
&lt;p&gt;But I think this is a really important work to do to bring more people to the
free software, so I've less time for hacking, but I think it's good, because
the fresh blood can do the hacking and if, after the Outreachy, one of the
interns  continues collaborating with GNOME, that will be more important for
the GNOME project that some new features in one app.&lt;/p&gt;
&lt;h3&gt;GNOME Translation Editor&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://teja.cetinski.eu/blog"&gt;Teja&lt;/a&gt; is the intern working in gtranslator. She is working in the
&lt;a href="https://l10n.gnome.org/"&gt;Damned Lies&lt;/a&gt; integration.&lt;/p&gt;
&lt;p&gt;Damned Lies is a web application for GNOME translators. This app provides
updated &lt;code&gt;.po&lt;/code&gt; file for each GNOME module and language and translators can
download and update that file using the web interface. That web is able to
do the commit to the original repository with the upload version from
translators.&lt;/p&gt;
&lt;p&gt;The idea is to provide a simple integration with this platform in the
GNOME Translation Editor app, using the web json API to be able to open &lt;code&gt;.po&lt;/code&gt;
files from the web directly without the need to download the file and then
open it.&lt;/p&gt;
&lt;p&gt;The current API of DL is really simple so we can't implement a real integration
without adding more functionality to this API. So this project requires some
work in the DL app too.&lt;/p&gt;
&lt;p&gt;In the future we can improve the integration adding the posibility to upload
the new &lt;code&gt;.po&lt;/code&gt; after saving to DL so translators doesn't need to go to the
web interface and can do all the translation flow using only the Translation
Editor.&lt;/p&gt;
&lt;h3&gt;Fractal&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://mairandom.space/"&gt;Maira&lt;/a&gt; is the intern working in Fractal. They are working in the initial
video preview widget.&lt;/p&gt;
&lt;p&gt;Fractal is a instant messaging app that works over matrix.org. Currently we
support different types of messages, like text, images, audio and files. But
for video we're using the same widget as we're using for files, so you can
download or open, but we've not a preview or inline player.&lt;/p&gt;
&lt;p&gt;The main idea of this project is to provide a simple video player using
gstreamer to play the video inside the Fractal app.&lt;/p&gt;
&lt;p&gt;This is not an easy task, because we're using Rust in Fractal and we need to
deal with bindings and language stuff, but I think it's doable.&lt;/p&gt;
&lt;p&gt;During the internship, Maira is also working fixing some bugs in the audio
player, becuase it uses gstreamer too, so during the code review, Maira
detected some problems and they are fixing it.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sun, 13 Jan 2019 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2019-01-13:/gnome-outreachy-2018.html</guid><category>blog</category><category>gnome</category><category>fractal</category><category>gtranslator</category><category>outreachy</category></item><item><title>Fractal December'18 Hackfest (part 2)</title><link>https://danigm.net/fractal-december-18-hackfest-2.html</link><description>&lt;p&gt;The Friday 14th was the last day of the &lt;a href="https://wiki.gnome.org/Hackfests/FractalDecember2018"&gt;second Fractal Hackfest&lt;/a&gt;. I've not
spend much time writing real code, the Thursday was mainly another hacking day
and I've been able to continue with the &lt;strong&gt;fractal-backend&lt;/strong&gt; creation, but
there's a lot of work to do there.&lt;/p&gt;
&lt;p&gt;But the hackfest was really productive, we've talked about big issues, project
management, some design ideas, new functionalities, the application refactor,
etc.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-2.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;GNOME newcomers experience&lt;/h3&gt;
&lt;p&gt;We talked about how to improve the GNOME newcomers experience and how to
improve the main view of Fractal. I think that Tobias will talk more about this,
he was working in some cool design for this and I think we can start to
implement this new views soon.&lt;/p&gt;
&lt;h3&gt;Best practices&lt;/h3&gt;
&lt;p&gt;We've been developing Fractal in a fast way, without spend a lot of time
thinking about the code quality, maintainability and that stuff. Recently we've
set the &lt;strong&gt;rustfmt&lt;/strong&gt; linter in the CI pipeline and we've some tests, but the
Merge Requests process wasn't defined and for example I was pushing directly
to master.&lt;/p&gt;
&lt;p&gt;To improve the quality of Fractal we've started a new wiki page to have a list
of &lt;a href="https://gitlab.gnome.org/GNOME/fractal/wikis/Best-practices-for-Fractal-development"&gt;best practices&lt;/a&gt; to follow. There we'll add some guidances on how we
should write code and the processes to follow to improve Fractal.&lt;/p&gt;
&lt;p&gt;We've decided that we should be more strict with the Merge Request code review
and now, direct push to master is not allowed, all changes will go through the
review process. We should wait at least two days to merge something and have
at least two people that approve the change.&lt;/p&gt;
&lt;p&gt;This will slow down the MR process, but will improve the code quality and
will reduce regressions. Any help is welcome, if you're able to test the MR,
you can leave a comment and other reviewers will have more confident in the
change.&lt;/p&gt;
&lt;p&gt;We're also working in the code quality using the cargo clippy tool. There's a 
Merge Request waiting for review, so we'll have a better rust source code soon.&lt;/p&gt;
&lt;h3&gt;Fractal is now in the GNOME group&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://gitlab.gnome.org/GNOME/fractal/"&gt;Fractal&lt;/a&gt; project was on the &lt;strong&gt;World&lt;/strong&gt; group inside the GNOME gitlab.
Fractal is a GNOME application, the most active developers are GNOME developers
and we try to follow the GNOME Human Interface Guidelines.&lt;/p&gt;
&lt;p&gt;Fractal is one of the first new applications that born just during the gitlab
migration so we go through a new process. At first Fractal was in my personal
gitlab under &lt;strong&gt;/danigm/fractal&lt;/strong&gt; then we move to the World group under
&lt;strong&gt;/World/fractal&lt;/strong&gt; and finally we're in the main GNOME group &lt;strong&gt;/GNOME/fractal&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;New release 4.0.0&lt;/h3&gt;
&lt;p&gt;The last release was the 3.30.0, more than three months has passed since this
release and we've a lot of changes so we want to provide a new stable release.&lt;/p&gt;
&lt;p&gt;We discuss a bit about the &lt;a href="https://gitlab.gnome.org/GNOME/fractal/issues/350"&gt;version number&lt;/a&gt; that we should follow and we
decided that we should do use our own version number system, because we want
to release as much as possible, when we've important changes.&lt;/p&gt;
&lt;p&gt;We're working in the &lt;a href="https://gitlab.gnome.org/GNOME/fractal/issues/396"&gt;4.0.0&lt;/a&gt; release, we're stabilizing and fixing important
bugs before the release and maybe we can have the new release during the next
week.&lt;/p&gt;
&lt;p&gt;So I've spend the last day looking for bugs and preparing the new release.&lt;/p&gt;
&lt;h3&gt;Matrix Live&lt;/h3&gt;
&lt;p&gt;We've a meeting with the people from Matrix.org to talk about Fractal and the
hackfest. You can view the full interview in youtube:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/SgyLHi8zZXQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h2&gt;Friday sponsored lunch&lt;/h2&gt;
&lt;p&gt;We've a sponsored lunch the Friday 14th, the local group &lt;a href="https://www.plan4d.eu"&gt;Plan4D&lt;/a&gt; invite
us to a great lunch in the city center.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.plan4d.eu/"&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/plan4d.jpg"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After that, I goes back to my home and leave the other people there in Seville
doing some tourism, I've to go back to Málaga.&lt;/p&gt;
&lt;p&gt;The hackfest was great and we have done a lot of things. This was the second
Fractal hackfest in 2018, and we meet at the GUADEC too. We've had two GSoC
students and now we've another intern thanks to the outreachy program. There's
a lot of people contributing to Fractal and that's great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GNOME is a great community&lt;/strong&gt; and I think that Matrix.org and Rust are helping
to this project a lot. The Matrix.org people are supporting us, indeed, Matthew
comes to the first hackfest and I think that the success of Fractal and the
community behind has a lot to thank to the Rust language and to the people
working in the Rust + GNOME integration, Gtk-rs is a great project.&lt;/p&gt;
&lt;p&gt;I want to apologize about the network problem during the hackfest. We've been
working all days thank to Julian network sharing, with eduroam, because we
aren't able to have guest access in the university.&lt;/p&gt;
&lt;p&gt;The university has a strict internet connection filtering, so only a professor
can ask for a guest connection for events and we do the request too late.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sun, 16 Dec 2018 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2018-12-16:/fractal-december-18-hackfest-2.html</guid><category>blog</category><category>gnome</category><category>fractal</category><category>hackfest</category><category>wadobo</category><category>seville</category></item><item><title>Fractal December'18 Hackfest (part 1)</title><link>https://danigm.net/fractal-december-18-hackfest-1.html</link><description>&lt;h2&gt;First two days of work&lt;/h2&gt;
&lt;p&gt;The Tuesday 11th started the &lt;a href="https://wiki.gnome.org/Hackfests/FractalDecember2018"&gt;second Fractal Hackfest&lt;/a&gt;. I've organized this
hackfest in Seville, the city where I studied computer science and here I've a
lot of friends in the University so is a good place to do it here.&lt;/p&gt;
&lt;p&gt;The weather was important too for the hackfest selection, in December
Seville is a good choice because the weather is not too cold, we're having
sunny days.&lt;/p&gt;
&lt;p&gt;The first day was a good day, thinking about some relevant issues and planning
what we want to do. We talked about the work needed for the interface split,
about the E2EE support, new features and the need for a new release.&lt;/p&gt;
&lt;p&gt;We're having some problems with the internet connection, because the University
has a restricted network policy and we ask for the guess internet connection
the Monday, but we're still waiting.&lt;/p&gt;
&lt;p&gt;Meantime we have to thanks to Julian Sparber the hackfest wifi, because
he's using his laptop to stream the eduroam connection.&lt;/p&gt;
&lt;h2&gt;Newcomers&lt;/h2&gt;
&lt;p&gt;The first day we try to promote as a newcomers day, and some people comes.
We've some contributions and I spend some time trying to help people to
introduce to the GNOME community.&lt;/p&gt;
&lt;h2&gt;GNOME Foundation sponsored dinner&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://gnome.org"&gt;GNOME Foundation&lt;/a&gt; payed for a Fractal dev day dinner, so we should
thanks this great dinner to GNOME:&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-dinner.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;We've a good time eating and drinking in the &lt;em&gt;Coco Verde&lt;/em&gt;. We talk about GNOME,
the desktop, matrix.org and other communication tools.&lt;/p&gt;
&lt;h1&gt;Hacking day&lt;/h1&gt;
&lt;p&gt;Today was a hacking day, so we started to work in the stuff that we talk about
yesterday. We've resolved some minor issues and I started with the
&lt;strong&gt;fractal-backend&lt;/strong&gt; crate.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-computer.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;We talked about move all the app data model and logic to the &lt;strong&gt;fractal-backend&lt;/strong&gt;
and leave &lt;strong&gt;fractal-gtk&lt;/strong&gt; only as UI management.&lt;/p&gt;
&lt;p&gt;We talked about the &lt;strong&gt;LMDB&lt;/strong&gt; use to store rooms and messages and we decided
that the best solution should be to use a relational database, because we've
relations and with the key-value thing we'll end creating those relations and
maintaining by hand. In any case, I've this decision in mind and I'm
implementing all this with a trait to hide the storage detail so we can change
easily in the future.&lt;/p&gt;
&lt;p&gt;So today I've spend a lot of time implementing this trait and a first
implementation for the &lt;strong&gt;Room&lt;/strong&gt; struct. I've decided to use &lt;strong&gt;rusqlite&lt;/strong&gt; instead
the &lt;strong&gt;diesel&lt;/strong&gt; orm because I want to keep it simple.&lt;/p&gt;
&lt;p&gt;I want to finish all the database storage and move the AppOp main loop to the
&lt;strong&gt;fractal-backend&lt;/strong&gt; and try to update the &lt;strong&gt;fractal-gtk&lt;/strong&gt; to use the backend
instead the AppOp struct to get the rooms and messages. But maybe is a lot of
work, I don't know if I'll be able to finish this before the end of the
hackfest.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-julian.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;This hackfest is possible because there's a lot of volunteer work and people
helping us. First of all, the Fractal core team that comes &lt;em&gt;Tobias Bernard&lt;/em&gt;,
&lt;em&gt;Julian Sparber&lt;/em&gt; and &lt;em&gt;Alexandre Franke&lt;/em&gt; and of course the &lt;em&gt;GNOME Foundation&lt;/em&gt;.
And also we've to thank &lt;em&gt;Alejandro Domínguez&lt;/em&gt;, a newcomer that is doing a really
good job fixing bugs and cleaning some old code.&lt;/p&gt;
&lt;p&gt;Then I want to thank the &lt;a href="https://sugus.eii.us.es/"&gt;Linux local group, SUGUS&lt;/a&gt; for the help and also
I want to thank to other free software related group &lt;a href="https://www.plan4d.eu"&gt;Plan4D&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.plan4d.eu/"&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/plan4d.jpg"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Plan4D is giving us some help with the place and cookies, fruits, juices and
some tea.&lt;/p&gt;
&lt;p&gt;And finally I want to thank again to my coworkers in &lt;a href="https://wadobo.com"&gt;wadobo&lt;/a&gt; because they
support me to spend a full work week working in gnome and they also spend some
time helping us to organize all the hackfest stuff.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Wed, 12 Dec 2018 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2018-12-12:/fractal-december-18-hackfest-1.html</guid><category>blog</category><category>gnome</category><category>fractal</category><category>hackfest</category><category>wadobo</category><category>seville</category></item><item><title>GNOME Gtk Developer</title><link>https://danigm.net/gnome-gtk-developer.html</link><description>&lt;h2&gt;I tried and I failed.&lt;/h2&gt;
&lt;p&gt;This summer, during the GUADEC 2018, the GNOME foundation announces some job
positions. One of that job was for a &lt;a href="https://www.gnome.org/foundation/careers/gtk-core-developer/"&gt;Gtk+ core developer&lt;/a&gt;. I tried to get
that job and after a long period I was rejected.&lt;/p&gt;
&lt;p&gt;The final developer selected for this possition is &lt;a href="https://mail.gnome.org/archives/foundation-list/2018-December/msg00000.html"&gt;Emmanuel Bassi&lt;/a&gt;, a very
active developer in the core of GNOME and the one that's behind the great
&lt;a href="https://www.bassi.io/articles/2018/10/25/the-history-of-gnome/"&gt;The History of GNOME Podcast&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To be honest, I really wanted that job. It's a dream job for me, working
fulltime in a free software project, by a foundation, with the great GNOME
technology and community.&lt;/p&gt;
&lt;p&gt;But I've to say that just when I saw the foundation announcement about this
position I thought about ebassi, because as far as I know, he's the best one to
do that job. In any case, I wanted to give a try and go for that job, I didn't
know about other people going for it, so maybe I can do it.&lt;/p&gt;
&lt;h2&gt;Some background&lt;/h2&gt;
&lt;p&gt;I'm working in my &lt;a href="https://wadobo.com"&gt;own little company&lt;/a&gt; since 2010. We're three coworkers
with the same percentage of the company, like a cooperative and we work mainly
as external consultant for bigger companies. We do a lot of work related with
GNU/Linux and free software, but lately we mainly do web apps with python
and react, because that's what our current clients ask for.&lt;/p&gt;
&lt;p&gt;But the first paid project for wadobo was a GNOME project, I started the
company with a small project to improve accessibility in PDF documents, working
directly in evince. And then we worked with Qt, QML and other technologies, but
I wasn't able to get a paid work on GNOME or Gtk+ since that initial work.&lt;/p&gt;
&lt;p&gt;In any case, I continued working in GNOME in my spare time, trying to
contribute, first with &lt;a href="https://gitlab.gnome.org/gnome/libgepub"&gt;libgepub&lt;/a&gt;, trying to bring epub support to evince,
and then to GNOME Books. And the last year I became more active because of
&lt;a href="https://gitlab.gnome.org/World/fractal"&gt;Fractal&lt;/a&gt; and &lt;a href="https://gitlab.gnome.org/gnome/gtranslator"&gt;Gnome Translation Editor (gtranslator)&lt;/a&gt;. And I've been
mentor for the GSoC 2018 and now I've two interns in the Outreachy program.&lt;/p&gt;
&lt;p&gt;I've some time to spend in GNOME, but I'm not making money from that, so now
I'm starting to look for that actively, because I really want to have more time
to spend working with free software.&lt;/p&gt;
&lt;p&gt;So, I talked with my coworkers, to be honest with them, and I told them that
I was going to try to get the Gtk+ core developer position.&lt;/p&gt;
&lt;h2&gt;The process&lt;/h2&gt;
&lt;p&gt;This was a long process, I think that it was long because there was a lot of
people for that position.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Email&lt;/strong&gt;: I sent the initial email in mid July, with a long description
   about me and my work in gnome and free software, with a little hope in my
   heart.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Survey&lt;/strong&gt;: Then I received an email with a link to a survey to complete with
   more information. That was great, because it sounds like I've passed the
   first cut.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Interview&lt;/strong&gt;: At the end of August I received a conglatulations email. My
   resume was positively reviewed and I'll have a personal interview.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That was really nice and I started to think that maybe it was possible after
   all.&lt;/p&gt;
&lt;p&gt;At the mid of September, I've a video conference meeting with Carlos Soriano
   and Matthias Clasen. For me was a great interview, talking about my experience,
   GNOME and Gtk+. Maybe the interview was a disaster and they are just a very
   nice people being nice with a poor developer, but at the end I thought that it
   was well and I was very exited about this.&lt;/p&gt;
&lt;h3&gt;The failure&lt;/h3&gt;
&lt;p&gt;After a long time without knowing anything, I received the email that I was
waiting for. I wasn't selected for the position. The next week, the foundation
announces the new staff member and in that moment I noticed that I've had no
chance to get the job. He's the better candidate and I'm really happy because
this is really good for GNOME.&lt;/p&gt;
&lt;h2&gt;And now what?&lt;/h2&gt;
&lt;p&gt;At the end of the process, I think that this was a great experience for me,
because this forces me to think about my career, what I want to do in the
future and I went through a full selection process, something that I've not done
since 2009.&lt;/p&gt;
&lt;p&gt;I will continue trying to find the way to work in GNOME or something realated
with the GNU/Linux desktop or core technologies.&lt;/p&gt;
&lt;p&gt;I did a lot of web development, I've a lot of experience and I'm really good doing
web with django, but I really want to go back and work with other technologies,
programming in C, the new Rust language and with some low level technologies.&lt;/p&gt;
&lt;p&gt;If someone need help, I'm here and I can work as a contractor. For now I'll
continue working in Wadobo, trying to find clients that requires desktop or
low level work.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Wed, 05 Dec 2018 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2018-12-05:/gnome-gtk-developer.html</guid><category>blog</category><category>gnome</category><category>gtk+</category><category>work</category><category>wadobo</category></item><item><title>GNOME Translation Editor 3.30.0</title><link>https://danigm.net/gnome-translation-editor.html</link><description>&lt;p&gt;I'm pleased to announce the new &lt;a href="https://download.gnome.org/sources/gtranslator/3.30/"&gt;&lt;strong&gt;GNOME Translation Editor&lt;/strong&gt;&lt;/a&gt; Release.
This is the new release of the well known &lt;strong&gt;Gtranslator&lt;/strong&gt;. I talked about
the &lt;a href="http://danigm.net/gtranslator-resurrection.html"&gt;&lt;strong&gt;Gtranslator Ressurection&lt;/strong&gt;&lt;/a&gt; some time ago and this is the result:&lt;/p&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator3.30.png"&gt;
        &lt;img src="/pictures/gtranslator3.30.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;This new release isn't yet in flathub, but &lt;a href="https://github.com/flathub/org.gnome.Gtranslator/pull/4"&gt;I'm working on it&lt;/a&gt; so we'll
have a flatpak version really soon. Meantime you can test using the gnome
nightly flatpak repo.&lt;/p&gt;
&lt;h2&gt;New release 3.30.0&lt;/h2&gt;
&lt;p&gt;This release doesn't add new functionality. The main change is in the code,
and in the interface.&lt;/p&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator1.png"&gt;
        &lt;img src="/pictures/gtranslator1.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;We've removed the toolbar and move the main useful buttons to the headerbar.
We've also removed the statusbar and replaced it with a new widget that shows
the document translation progress.&lt;/p&gt;
&lt;p&gt;The plugin system and the dockable window system has been removed to simplify
the code and make the project more maintenable. The only plugin that is
maintained for now is the translation memory, that's now integrated. I'm
planning to migrate other useful plugins, but that's for the future.&lt;/p&gt;
&lt;p&gt;Other minor changes that we've made are in the message table, we've removed
some columns and now we only show two, the original message and the translated
one and we use colors and text styles to show fuzzy status and untranslated.&lt;/p&gt;
&lt;p&gt;The main work is a full code modernization, now we use meson to build, we've
flatpak integration and this simplify the development because gtranslator know
works by default in Gnome Builder without the need to install development
dependencies.&lt;/p&gt;
&lt;p&gt;There's others minor changes like the new look when you open the app without
any file:&lt;/p&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator-open.png"&gt;
        &lt;img src="/pictures/gtranslator-open.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Or the new language selector that autofill all the profile fields using the
language:&lt;/p&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator-lang.png"&gt;
        &lt;img src="/pictures/gtranslator-lang.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;And for sure we've tried to fix most important bugs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.gnome.org/GNOME/gtranslator/issues/1"&gt;Search and replace deletes all fuzzy states&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.gnome.org/GNOME/gtranslator/issues/5"&gt;Check files before the save&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.gnome.org/GNOME/gtranslator/issues/19"&gt;msgctxt should make strings different&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.gnome.org/GNOME/gtranslator/issues/8"&gt;Some headers get line breaks and don't follow the right format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.gnome.org/GNOME/gtranslator/issues?scope=all&amp;amp;utf8=%E2%9C%93&amp;amp;state=closed"&gt;And more&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;New name and new Icon&lt;/h2&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator-icon.png"&gt;
        &lt;img src="/pictures/gtranslator-icon.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Following the modern GNOME app names, we've renamed the app from Gtranslator
to &lt;strong&gt;GNOME Translation Editor&lt;/strong&gt;. Internally we'll continue with the gtranslator
name, so the app is gtranslator, but for the final user the name will be
&lt;strong&gt;Translation Editor&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And following the &lt;a href="https://gitlab.gnome.org/GNOME/Initiatives/issues/2"&gt;App Icon Redesign Initiative&lt;/a&gt; we've a new icon that
follows the new &lt;a href="https://developer.gnome.org/hig/stable/icon-design.html.en"&gt;HIG&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;I'm not doing this alone. I became the gtranslator maintainer because Daniel
Mustieles push to have a modern tool for GNOME translators, done with gnome
technology and fully functional.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;GNOME Translation Editor&lt;/strong&gt; is a project done by the GNOME community, there
are other people helping with code, documentation, testing, design ideas and
much more and any help is always welcome. If you're insterested, don't hesitate
and come to the &lt;a href="https://gitlab.gnome.org/GNOME/gtranslator"&gt;gnome gitlab&lt;/a&gt; and collaborate with this great project.&lt;/p&gt;
&lt;p&gt;And maybe it's a bit late, but I've publish a project to the &lt;a href="https://www.outreachy.org/apply/project-selection/"&gt;outreachy.org&lt;/a&gt;,
so maybe someone can work on this as an intern for three months. I'll try to
get more people involved here using following outreachy and maybe GSoC, so if
you're a student, now is the right time to start contributing to be able to be
selected for the next year internship programs.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 03 Nov 2018 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2018-11-03:/gnome-translation-editor.html</guid><category>blog</category><category>gnome</category><category>gtranslator</category></item><item><title>Gtranslator Resurrection</title><link>https://danigm.net/gtranslator-resurrection.html</link><description>&lt;p&gt;The last week I received a telegram message about &lt;a href="https://wiki.gnome.org/Apps/Gtranslator"&gt;Gtranslator&lt;/a&gt;, that was
unmaintained for a long time. GNOME translators uses different tools to
translate .po files, Gtranslator is a tool for translator that is integrated
with the GNOME desktop, but with the time, Gtranslator is getting old and there
are several known bugs that never get fixed.&lt;/p&gt;
&lt;p&gt;So I decided to go ahead and become the maintainer of Gtranslator with the main
idea of update the interface and fix mayor bugs.&lt;/p&gt;
&lt;h2&gt;Why we should care about Gtranslator&lt;/h2&gt;
&lt;p&gt;PO files are plain text files that can be edited with any text editor, but
there're a lot of tools for translator to simplify the edition of .po files,
these way the program will avoid formatting problems and can also provide some
tools to help in the translation.&lt;/p&gt;
&lt;p&gt;There's a lot of tools to edit .po files, so, why we need another application?
why translators can't pick another useful tool if Gtranslator is unmaintained?&lt;/p&gt;
&lt;p&gt;The main reason is the same as we've a text editor or the gnome-builder or other
developer tools, because all users want to use applications integrated in the
desktop, so it always will be better for a GNOME user a native GNOME app to edit
.po files than a Qt one or a webpage.&lt;/p&gt;
&lt;p&gt;The other reason is that we need to simplify the translators life to have better
and more translations and that can only be done if we're developping the tool
that translator will use, because in the future we can integrate Gtranslator
with the gnome gitlab, or with &lt;a href="https://l10n.gnome.org/"&gt;Damned Lies&lt;/a&gt;, to download or upload
translation files.&lt;/p&gt;
&lt;h2&gt;What's the plan&lt;/h2&gt;
&lt;p&gt;Gtranslator has a GNOME 2.0 interface, with toolbar, menus, statusbar and
dockable widgets:&lt;/p&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator1.png"&gt;
        &lt;img src="/pictures/gtranslator1.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;But in the git repo, the main branch was &lt;em&gt;broken&lt;/em&gt;. Someone starts a big
refactor, in 2015, to try to update the interface to look more like a modern
GNOME 3 application but that refactor never ended so the master branch compiles,
but doesn't do anything. There was a lot of widgets that was half ported to a
modern code.&lt;/p&gt;
&lt;p&gt;There's also some &lt;a href="https://wiki.gnome.org/Design/Apps/Translator"&gt;designs&lt;/a&gt; to modernize the interface, but the work wasn't
finished.&lt;/p&gt;
&lt;p&gt;My plan is to continue that work, modernize the UI, remove all &lt;em&gt;deprecated&lt;/em&gt; code
and warnings and then continue the development from there.&lt;/p&gt;
&lt;p&gt;I don't want to spend a lot of time reworking the whole app to have a new one in
two months, I want to have a functional app as soon as possible, and continue
with a continuous development, improving the UI and the functionality needed by
the translator team.&lt;/p&gt;
&lt;p&gt;So the roadmap that I've in mind is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fix the current master state&lt;/li&gt;
&lt;li&gt;Fix the known bugs&lt;/li&gt;
&lt;li&gt;Redesign the interface&lt;/li&gt;
&lt;li&gt;Enhance linking with Damned Lies&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;What I've done in one week&lt;/h2&gt;
&lt;p&gt;I started updating the build system, from autotools to meson build, and then
I've added a flatpak manifest file, so now gnome-builder is able to build
Gtranslator using meson and flatpak and it's easier to develop this way.&lt;/p&gt;
&lt;p&gt;This build tools modernization is great because with the new gitlab we've now
continuous integration, and that means that the gitlab compiles and generates a
flatpak bundle for each commit, making it easier for testers to test new
functionality or Merge Requests, you only need to download the .flatpak and
install locally, no need to build at all.&lt;/p&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator-gitlab.png"&gt;
        &lt;img src="/pictures/gtranslator-gitlab.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Then I started to fix the master branch. There was a &lt;em&gt;project&lt;/em&gt; selector view and
then you go to the translation view. There was only the .ui files and not
working yet, so I reworked that a bit and convert the &lt;em&gt;project&lt;/em&gt; selector to a
&lt;em&gt;file&lt;/em&gt; selector for now and then when you select a file, you've almost the same
old interface, but without the menu, toolbar and statusbar.&lt;/p&gt;
&lt;p style="text-align: center" class="img"&gt;
    &lt;a href="/pictures/gtranslator2.png"&gt;
        &lt;img src="/pictures/gtranslator2.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I've been moving the toolbar icons to the headerbar so we can have the main
functionality working. We've now a functional application.&lt;/p&gt;
&lt;h2&gt;Plugins&lt;/h2&gt;
&lt;p&gt;Gtranslator has a plugin system and has eight plugins, but that plugins was
disabled some time ago. So I've started to recover that functionality but
instead of as plugins, I've started to move the plugins to the core app code.&lt;/p&gt;
&lt;p&gt;The only plugin ported currently is the translation memory. I'll take a look to
the other plugins and view if it's desirable to reimplement that or remove that
code.&lt;/p&gt;
&lt;h2&gt;What's next&lt;/h2&gt;
&lt;p&gt;All the statusbar functionality is missing right now, I need to find some place
in the new UI to show that.&lt;/p&gt;
&lt;p&gt;I wan't to start as soon as possible to work in a new widget to show messages,
instead of the current table.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;project&lt;/em&gt; selection part is a bit hard because I need to think about what's
a project in this context. Currently Gtranslator is a .po file editor, but if we
change that to manage &lt;em&gt;projects&lt;/em&gt; like a group of .po files, I need to think
about how to store that information and the easier way for translator to use the
interface.&lt;/p&gt;
&lt;p&gt;So if you've time and want to contribute in this project, go ahead, you can ask
in the #gtranslator IRC channel or talk to me directly. Currently there's no
a lot of issues in the gitlab, but as soon as I get a functional version in
gnome-nightly flatpak repo and some translators starts to use it, we'll have a
lot of things to do.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Mon, 10 Sep 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-09-10:/gtranslator-resurrection.html</guid><category>blog</category><category>gnome</category><category>gtranslator</category></item><item><title>GIR support in gnome-class</title><link>https://danigm.net/gnome-class-gir.html</link><description>&lt;p&gt;Recently I've been working again in the &lt;a href="http://danigm.net/libgepub-rust.html"&gt;rust port of libgepub&lt;/a&gt;, libgepub
is C code, but in the &lt;a href="https://gitlab.gnome.org/GNOME/libgepub/tree/rust-migration/"&gt;&lt;code&gt;rust-migration&lt;/code&gt; branch&lt;/a&gt; almost all the real
functionality is done with rust and the &lt;code&gt;GepubDoc&lt;/code&gt; class is a &lt;code&gt;GObject&lt;/code&gt; wrapper
around that code.&lt;/p&gt;
&lt;p&gt;For this reason I was thinking about to use &lt;a href="https://gitlab.gnome.org/federico/gnome-class"&gt;&lt;code&gt;gnome-class&lt;/code&gt;&lt;/a&gt; to implement
&lt;code&gt;GepubDoc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Gnome-class is a rust lib to write GObject code in rust that's compatible with
the C binary API so then you can call this new GObject code written with
gnome-class from C. I've worked a little in gnome-class, implementing a
&lt;a href="http://danigm.net/rust-gnome.html"&gt;basic properties support&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;GObject Introspection&lt;/h2&gt;
&lt;p&gt;A great advantage of write GObject code in C is that you can have automatic
bindings using &lt;a href="https://gi.readthedocs.io/en/latest/"&gt;GObject Introspection&lt;/a&gt;. This is great because you only need
to write the C code, write correctly the documentation of the public API and
then you'll get GIR for free, and that means that you can use your lib from
&lt;code&gt;gjs&lt;/code&gt;, &lt;code&gt;python&lt;/code&gt; and other automatic bindings.&lt;/p&gt;
&lt;p&gt;GIR defines a simple XML document to declare the public API and there is the
meta information needed for bindings, to know how to call the lib functions and
how to convert params and also provides memory management information, for
languages with garbage collector and so. These files are places under
&lt;code&gt;/usr/share/gir-1.0/&lt;/code&gt; and it looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;get_cover&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;c:identifier=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;gepub_doc_get_cover&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;return-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;transfer-ownership=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;full&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;xml:space=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;preserve&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;cover&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;path&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;retrieve&lt;span class="w"&gt; &lt;/span&gt;with
gepub_doc_get_resource&lt;span class="nt"&gt;&amp;lt;/doc&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf8&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;c:type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;gchar*&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/return-value&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;parameters&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;instance-parameter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;doc&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;transfer-ownership=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;none&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;xml:space=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;preserve&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;#GepubDoc&lt;span class="nt"&gt;&amp;lt;/doc&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Doc&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;c:type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GepubDoc*&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/instance-parameter&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/parameters&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/method&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This xml is &lt;em&gt;compiled&lt;/em&gt; to a binary format to be used from bindings and those
compiled gir files are places under &lt;code&gt;/usr/lib64/girepository-1.0/&lt;/code&gt; with the
&lt;code&gt;.typelib&lt;/code&gt; extension.&lt;/p&gt;
&lt;p&gt;This is great, and the &lt;code&gt;GepubDoc&lt;/code&gt; class is exported as GIR and indeed it's used
from javascript code in the gnome-books application using this. If we want to
migrate libgepub from C to rust, we need to provide this GIR to don't break
the compatibility in other apps. That's done currently in the rust-migration
branch by hand, creating the &lt;code&gt;GepubDoc&lt;/code&gt; in C and calling the rust code from
there.&lt;/p&gt;
&lt;p&gt;With gnome-class it should be possible to write that &lt;code&gt;GepubDoc&lt;/code&gt; code in rust
and have the same ABI, but currently gnome-class doesn't generate GIR, that's
a little problem that I can try to solve using my dev skills.&lt;/p&gt;
&lt;h2&gt;Adding GIR to gnome-class&lt;/h2&gt;
&lt;p&gt;So, libgepub is the excuse to start to implement GIR in gnome-class. There's
an &lt;a href="https://gitlab.gnome.org/federico/gnome-class/issues/8"&gt;issue&lt;/a&gt; in the gitlab with an initial syntax proposal and I started from
that:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#[generate_gir(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo.gir&amp;quot;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;
&lt;span class="cp"&gt;#[generate_c_header(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo.h&amp;quot;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;gobject_gen&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo.gir&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo.h&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&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 first thing to do is to add a way to tell that we want to generate the GIR
file in the class definition. In this proposal, the GIR tag is before the
&lt;code&gt;gobject_gen&lt;/code&gt; proc-macro, that can't be done, or I don't know how to do that
easily, so I changed to a declaration inside the proc-macro, that's what we're
parsing in gnome-class:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;gobject_gen&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#[generate_gir(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Counter.gir&amp;quot;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&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="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&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="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="bp"&gt;self&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="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&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="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&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;With the code insithe de &lt;code&gt;gobject_gen&lt;/code&gt; I only need to update the parser to
allow this new syntax:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;named&lt;/span&gt;&lt;span class="p"&gt;!{&lt;/span&gt;&lt;span class="nx"&gt;parse_gir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LitStr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;do_parse&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;punct&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;brackets&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;do_parse&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;generate_gir&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parens&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;LitStr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="m m-Double"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="m m-Double"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&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;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Synom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;named&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;do_parse&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;gir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;parse_gir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                                    &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;class&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;                                  &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;Ident&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                                        &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;do_parse&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;punct&lt;/span&gt;&lt;span class="p"&gt;!(:)&lt;/span&gt;&lt;span class="w"&gt;                                            &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;superclass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// FIXME: interfaces&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;superclass&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="nx"&gt;FieldsNamed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;gir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&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, I'm storing the GIR file name in the ast class so I can check if that
field is &lt;code&gt;None&lt;/code&gt; or &lt;code&gt;Some&lt;/code&gt; and generate the GIR in that case.&lt;/p&gt;
&lt;h2&gt;Generating the XML&lt;/h2&gt;
&lt;p&gt;The parser was the easier part, we've now the hability to define if we want
to generate the GIR file and we're storing that information in the High-level
Internal Representation (hir).&lt;/p&gt;
&lt;p&gt;To generate the XML I've to add a new call before generating the code:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;    let result: Result&amp;lt;proc_macro2::TokenStream&amp;gt; =
&lt;span class="gd"&gt;-        hir::Program::from_ast_program(&amp;amp;ast_program).and_then(|program| Ok(gen::codegen(&amp;amp;program)));&lt;/span&gt;
&lt;span class="gi"&gt;+        hir::Program::from_ast_program(&amp;amp;ast_program)&lt;/span&gt;
&lt;span class="gi"&gt;+            .and_then(|program| {&lt;/span&gt;
&lt;span class="gi"&gt;+                gen::gir::generate(&amp;amp;program)?;&lt;/span&gt;
&lt;span class="gi"&gt;+                Ok(gen::codegen(&amp;amp;program))&lt;/span&gt;
&lt;span class="gi"&gt;+            });&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    match result {
&lt;span class="w"&gt; &lt;/span&gt;        Ok(tokens) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href="https://gitlab.gnome.org/danigm/gnome-class/tree/gir/src/gen/gir.rs"&gt;&lt;code&gt;gir&lt;/code&gt;&lt;/a&gt; module generates the XML, iterating over all classes defined in
the program.&lt;/p&gt;
&lt;p&gt;The XML isn't really hard to generate, we've almost all the information so we
can iterate over all the class methods and add to the XML. There's some
information that's not really easy to get so I've let some &lt;em&gt;TODO&lt;/em&gt; in this
initial version, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find the parent class name (GObject.Object by default)&lt;/li&gt;
&lt;li&gt;Get the library version and shared-lib path (lib#CLASSNAME#1.0.0 by default)&lt;/li&gt;
&lt;li&gt;Get dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So this is not a full featured GIR support, but it's an initial step to get
it, and it &lt;em&gt;works&lt;/em&gt;, at least with simple examples :D&lt;/p&gt;
&lt;h2&gt;Example project&lt;/h2&gt;
&lt;p&gt;To test that this is working correctly I've created a
&lt;a href="https://gitlab.gnome.org/danigm/gnome-class-example/"&gt;simple example project&lt;/a&gt;, that uses gnome-class, generates the gir and
has an small script to call the generated code from python.&lt;/p&gt;
&lt;p&gt;This is the rust code:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;gobject_gen&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;generate_gir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Counter.gir&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&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;I've a bash script to compile and generate the typelib file from the xml,
generated by gnome-class:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cargo&lt;span class="w"&gt; &lt;/span&gt;+nightly&lt;span class="w"&gt; &lt;/span&gt;build
g-ir-compiler&lt;span class="w"&gt; &lt;/span&gt;Counter.gir&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Counter-1.0.typelib
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And I've written a test in python using &lt;code&gt;gi.repository&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gi.repository&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&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;def&lt;/span&gt; &lt;span class="nf"&gt;test_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&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="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&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="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is working right now, so we're getting closer to the final goal of having
libgepub written with rust code and have GIR generation.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 01 Sep 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-09-01:/gnome-class-gir.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>gnome-class</category><category>GIR</category></item><item><title>libgepub + rust</title><link>https://danigm.net/libgepub-rust.html</link><description>&lt;p&gt;In 2010 I was working with evince, the gnome PDF document viewer, trying to
add some accessibility to PDF files. That was really hard, not because GTK+ or
ATK technology but because the PDF format itself. The PDF format is really cool
for printing because you know that the piece of paper will look the same as the
PDF doc, and because it's vector it scales and don't loose quality and files
are smaller than image files, but almost all PDF files have not any metadata for
sections, headings, tablets or so, this depends on the creation tool, but it's
really hard to deal with PDF content text, because you don't know event if the
text that you're reading is really in the same order that you read from the
PDF.&lt;/p&gt;
&lt;p&gt;After my fight against the PDF format hell and poppler, I discovered the epub
format that's a really simple format for electronic books. An epub is a zip
with some XML files describing the book index and every chapter is a xhtml and
xhtml is a good format compared to PDF because you can parse easily with any
XML lib and the content is tagged and well structured so you know what's a
heading, what's a paragraph, etc.&lt;/p&gt;
&lt;p&gt;So I started to write a simple C library to read epub files, thinking about
add epub support to evince. That's how &lt;a href="https://gitlab.gnome.org/GNOME/libgepub"&gt;libgepub&lt;/a&gt; was born. I tried to
&lt;a href="https://github.com/danigm/evince/tree/epub"&gt;integrate libgepub in evince&lt;/a&gt;, I've something working, rendering with
webkit, but nothing really useful, because evince needs pages and it's not
easy to split an xhtml file in pages with the same height, because xhtml is
continuous text and it adapts to the page width, so I give up and leave this
branch.&lt;/p&gt;
&lt;h2&gt;gnome-books&lt;/h2&gt;
&lt;p&gt;&lt;center&gt;
    &lt;img src="https://wiki.gnome.org/Apps/Books?action=AttachFile&amp;do=get&amp;target=books-window.png" alt="gnome-books" width="100%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;After some time, I discovered &lt;a href="https://wiki.gnome.org/Apps/Books"&gt;gnome-books&lt;/a&gt;, and the idea of epub support
comes to me again. Books has an initial &lt;em&gt;epub&lt;/em&gt; support, that consists only in
showing in the collection, but when you click on the book, an error message
was shown because there's no epub preview support.&lt;/p&gt;
&lt;p&gt;I've libgepub with GIR support and books was written with gjs so it was easy
for me to add an initial support for epub documents using libgepub and
rendering with webkit.&lt;/p&gt;
&lt;p&gt;So libgepub is used in a gnome app to render epub documents and for that reason
gnome &lt;em&gt;depends&lt;/em&gt; on libgepub now.&lt;/p&gt;
&lt;h2&gt;Rustifying everything&lt;/h2&gt;
&lt;p&gt;In 2017 I discover &lt;a href="https://www.rust-lang.org/"&gt;rust&lt;/a&gt; and fell in love with this language, so I wanted
to write some code with rust and I started porting this epub library and I
ended creating the &lt;a href="https://crates.io/crates/epub"&gt;epub crate&lt;/a&gt;, that's basically the same implementation
but with rust instead of C, the API is really similar.&lt;/p&gt;
&lt;p&gt;After that, I read somewhere that Federico was porting the &lt;a href="https://gitlab.gnome.org/GNOME/librsvg"&gt;librsvg&lt;/a&gt;
code to rust, so I thought that I can do the same with libgepub and replace
almost all the C code with this new rust crate.&lt;/p&gt;
&lt;p&gt;I copied all the code for autotools to build using cargo and I've copied to
the &lt;em&gt;glue&lt;/em&gt; code from librsvg and adapt to my own lib. That worked really well
and I was able to remove the core libgepub C code and replace with the new
rust code.&lt;/p&gt;
&lt;p&gt;But rust is really new and it was not full supported in all distributions, so
release depending on rust will make the release people's live harder. That's
the reason because I decided to leave the C version for now and wait a little
to make the rust migration.&lt;/p&gt;
&lt;p&gt;And here we're, the libgepub has changed a little, some fixes and small changes,
but the main change is that now we're using meson to build instead of autotools,
and it's not easy to integrate a rust+cargo lib in the meson desc. There's a
&lt;a href="https://github.com/mesonbuild/meson/pull/2617"&gt;PR in meson to support this&lt;/a&gt; but it seems that meson devs doesn't like the
idea.&lt;/p&gt;
&lt;p&gt;So yesterday I was playing around with meson to get back the rust code working
in libgepub, and was not easy, but now I've a &lt;a href="https://gitlab.gnome.org/GNOME/libgepub/tree/rust-migration"&gt;working build configuration&lt;/a&gt;
that works. It's a hack with a custom script to build with cargo, but it's
working so I'm really happy.&lt;/p&gt;
&lt;h2&gt;Show me the code&lt;/h2&gt;
&lt;p&gt;To integrate the rust epub crate in the libgepub C lib I needed to write a
simple cargo project with the &lt;em&gt;glue&lt;/em&gt; code that converts the rust types to
glib/c types and expose that in a C lib, and that's the &lt;a href="https://gitlab.gnome.org/GNOME/libgepub/tree/rust-migration/subprojects/libgepub_internals"&gt;libgepub_internals&lt;/a&gt;,
that's full of glue rust+glib code like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#[no_mangle]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;epub_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;c_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EpubDoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;from_glib_none&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EpubDoc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;into_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[no_mangle]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;epub_destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EpubDoc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;raw_doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_null&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;from_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_doc&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, in the &lt;code&gt;gepub-doc.c&lt;/code&gt;, that's the glib object that expose the GIR API,
I added calls to this functions, defining each function heading that'll be
used from the rust static lib at link time:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Rust&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nf"&gt;epub_destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_resource_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mdata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_resource_mime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_resource_mime_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_current_mime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_current_with_epub_uris&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nf"&gt;epub_set_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;guint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;guint&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;epub_get_num_pages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;guint&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;epub_get_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;gboolean&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nf"&gt;epub_next_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;gboolean&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nf"&gt;epub_prev_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_cover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_resource_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_current_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_current_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_get_resources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;guint&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;epub_resources_get_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;er&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;gchar&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_resources_get_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;er&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;gchar&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_resources_get_mime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;er&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;gchar&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;epub_resources_get_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;er&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then calling to this functions:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gboolean&lt;/span&gt;
&lt;span class="nf"&gt;gepub_doc_initable_init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GInitable&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;initable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="n"&gt;GCancellable&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cancellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="n"&gt;GError&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;GepubDoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GEPUB_DOC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;g_assert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rust_epub_doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;epub_new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rust_epub_doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;g_set_error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gepub_error_quark&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GEPUB_ERROR_INVALID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Invalid epub file: %s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TRUE&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;To make this work, I needed to add the &lt;code&gt;libgepub_internals&lt;/code&gt; dependency to the
libgepub meson.build like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gepub_deps = [
  dependency(&amp;#39;gepub_internals&amp;#39;, fallback: [&amp;#39;libgepub_internals&amp;#39;, &amp;#39;libgepub_internals_dep&amp;#39;]),
  dependency(&amp;#39;webkit2gtk-4.0&amp;#39;),
  dependency(&amp;#39;libsoup-2.4&amp;#39;),
  dependency(&amp;#39;glib-2.0&amp;#39;),
  dependency(&amp;#39;gobject-2.0&amp;#39;),
  dependency(&amp;#39;gio-2.0&amp;#39;),
  dependency(&amp;#39;libxml-2.0&amp;#39;),
  dependency(&amp;#39;libarchive&amp;#39;)
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This definition looks for the gepub_internals lib in the libgepub_internals
subproject with this meson.build:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;libgepub_internals&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;rust&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="m m-Double"&gt;3.29.6&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;license&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;GPLv3&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;libgepub_internals_version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;meson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_version&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;version_array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;libgepub_internals_version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;libgepub_internals_major_version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;version_array&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;to_int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;libgepub_internals_minor_version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;version_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;to_int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;libgepub_internals_version_micro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;version_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;to_int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;libgepub_internals_prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;cargo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;find_program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;cargo&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cargo_vendor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;find_program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;cargo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;vendor&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cargo_script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;find_program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;scripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cargo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;grabber&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;find_program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;scripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;grabber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cargo_release&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;find_program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;scripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;release&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grabber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;cargo_build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;custom_target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;cargo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;build_by_default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;libgepub_internals&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cargo_script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;@&lt;/span&gt;&lt;span class="nx"&gt;CURRENT_SOURCE_DIR&lt;/span&gt;&lt;span class="err"&gt;@&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;@&lt;/span&gt;&lt;span class="nx"&gt;OUTPUT&lt;/span&gt;&lt;span class="err"&gt;@&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nx"&gt;libgepub_internals_lib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;static_library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;gepub_internals&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cargo_build&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;cc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;meson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_compiler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;libdl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find_library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;libgepub_internals_dep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;declare_dependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;link_with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;libgepub_internals_lib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;libdl&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cargo_build&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'm using here a custom_target to build the lib using a custom script that
simply calls to cargo and then copies the result lib to the correct place:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DEBUG MODE&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;cargo&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;--manifest-path&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;/Cargo.toml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;/target/debug/libgepub_internals.a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;.a
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;RELEASE MODE&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;cargo&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;--manifest-path&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;/Cargo.toml&lt;span class="w"&gt; &lt;/span&gt;--release&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;/target/release/libgepub_internals.a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;.a
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then I declared the &lt;code&gt;static_library&lt;/code&gt; and the dependency with
&lt;code&gt;declare_dependency&lt;/code&gt;. I need to add &lt;code&gt;threads&lt;/code&gt; and &lt;code&gt;dl&lt;/code&gt; because the epub crate
depends on it and this works!&lt;/p&gt;
&lt;p&gt;I'll need to vendor all dep crates with cargo-vendor for releasing, but I think
that this is working and it's the way to go with libgepub.&lt;/p&gt;
&lt;h2&gt;The future of libgepub + rust&lt;/h2&gt;
&lt;p&gt;Currently, with the libgepub_internals lib, the &lt;code&gt;gepub-doc.c&lt;/code&gt; code is
basically to provide a &lt;code&gt;gobject&lt;/code&gt; and GIR information to be able to work with
epub docs, but the real work is done in Rust. The &lt;a href="https://gitlab.gnome.org/federico/gnome-class"&gt;gnome-class&lt;/a&gt; provides a
simple way to build this &lt;code&gt;gobject&lt;/code&gt; with rust code, but currently it's not
completed and there's no way to generate GIR, but in the future, it could be
cool to remove the &lt;code&gt;gepub-doc.c&lt;/code&gt; code and generate all with &lt;code&gt;gnome-class&lt;/code&gt;.
I can wait until Federico writes the piece of code that I need for this or maybe
I should contribute to &lt;code&gt;gnome-class&lt;/code&gt; to be able to do this.&lt;/p&gt;
&lt;p&gt;Libgepub also provides a widget that inherits from WebkitWebView to render the
book. That widget is written in C to provide GIR data also and we can try to
do the same and use &lt;code&gt;gnome-class&lt;/code&gt; to write this widget.&lt;/p&gt;
&lt;p&gt;But for now, we're really far from this, we need to spend some time improving
&lt;code&gt;gnome-class&lt;/code&gt; to be able to write all the code in &lt;strong&gt;rust&lt;/strong&gt; and expose the
gobject GIR. Meantime we can start to use rust with this &lt;strong&gt;glue&lt;/strong&gt; code, and
that's great, because if you've a gobject library and you want to migrate to
rust, you don't need to migrate all the code at once, you can do the same that
librsvg is doing and migrate function by function and that's really cool.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Thu, 09 Aug 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-08-09:/libgepub-rust.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>libgepub</category><category>gnome-books</category><category>epub</category></item><item><title>mdl</title><link>https://danigm.net/mdl.html</link><description>&lt;p&gt;The last month I wrote a blog post about the &lt;a href="http://danigm.net/lmdb.html"&gt;LMDB Cache database&lt;/a&gt; and my
wish to use that in Fractal. To summarize, LMDB is a memory-mapped key-value
database that persist the data to the filesystem. I want to use this in the
Fractal desktop application to replace the current state storage system
(we're using simple json files) and as a side effect we can use this storage
system to share data between threads because currently we're using a big
struct &lt;code&gt;AppOp&lt;/code&gt; shared with &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;AppOp&amp;gt;&amp;gt;&lt;/code&gt; and this cause some problems
because we need to share and lock and update the state there.&lt;/p&gt;
&lt;p&gt;The main goal is to define an app data model with smaller structs and store
this using LMDB, then we can access to the same data querying the LMDB and we
can update the app state storing to the LMDB.&lt;/p&gt;
&lt;p&gt;With this change we don't need to share these structs, we only need to query
to the LMDB to get the data and the work with that, and this should simplify
our code. The other main benefit will be that we'll have this state in the
filesystem by default so when we open the app after close, we'll stay in the
same state.&lt;/p&gt;
&lt;p&gt;Take a look to the &lt;a href="https://gitlab.gnome.org/danigm/mdl/blob/master/examples/gtkapp/src/main.rs"&gt;gtk TODO example app&lt;/a&gt; to view how to use &lt;em&gt;mdl&lt;/em&gt; with
signals in a real gtk app.&lt;/p&gt;
&lt;h2&gt;What is mdl&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://crates.io/crates/mdl"&gt;mdl&lt;/a&gt; is Data model library to share app state between threads and process
and persist the data in the filesystem. Implements a simple way to store
structs instances in a LMDB database and other methods like BTreeMap.&lt;/p&gt;
&lt;p&gt;I started to play with the LMDB rust binding and writing some simple tests.
After some simple tests, I decided to write a simple abstraction to hide the
LMDB internals and to provide a simple data storage and to do that I created
the &lt;strong&gt;mdl&lt;/strong&gt; crate.&lt;/p&gt;
&lt;p&gt;The idea is to be able to define your app model as simple rust structs. LMDB is
a key-value database so every struct instance will have an unique &lt;em&gt;key&lt;/em&gt; to
store in the cache.&lt;/p&gt;
&lt;p&gt;The keys are stored in the cache ordered, so we can use some techniques to
store related objects and to retrieve all objects of a kind, we only need to
build keys correctly, following an scheme. For example, for fractal we can
store rooms, members and messages like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rooms with key "room:roomid", to store all the room information, title,
   topic, icon, unread msgs, etc.&lt;/li&gt;
&lt;li&gt;members with key "member:roomid:userid", to store all member information.&lt;/li&gt;
&lt;li&gt;messages with key "msg:roomid:msgid" to store room messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Following this key assignment we can iterate over all rooms by querying all
objects that starts with "room", we can get all members and all messages from
a room.&lt;/p&gt;
&lt;p&gt;This have some inconveniences, because we can't query directly an message by
id if we don't know the roomid. If we need that kind of queries, we need to
think about another key assignment or maybe we should duplicate data. key-value
are simple databases so we don't have the power of relational databases.&lt;/p&gt;
&lt;h2&gt;Internals&lt;/h2&gt;
&lt;p&gt;LMDB is fast and efficient, because it's in memory so using this cache won't
add a lot of overhead, but to make it simple to use I've to add some overhead,
so mdl is easy by default and can be tuned to be really fast.&lt;/p&gt;
&lt;p&gt;This crate has three main modules with traits to implement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;model&lt;/strong&gt;: This contains the &lt;code&gt;Model&lt;/code&gt; trait that should implement every
   struct that we want to make cacheable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;store&lt;/strong&gt;: This contains the &lt;code&gt;Store&lt;/code&gt; trait that's implemented by all the
   cache systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;signal&lt;/strong&gt;: This contains the &lt;code&gt;Signaler&lt;/code&gt; trait and two structs that allow
   us to emit/subscribe to "key" signals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And two more modules that implements the current two cache systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cache&lt;/strong&gt;: LMDB cache that implements the &lt;code&gt;Store&lt;/code&gt; trait.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bcache&lt;/strong&gt;: BTreeMap cache that implements the &lt;code&gt;Store&lt;/code&gt; trait. This is a good
   example of other cache system that can be used, this doesn't persist to the
   filesystem.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we've two main concepts here, the &lt;strong&gt;Store&lt;/strong&gt; and the &lt;strong&gt;Model&lt;/strong&gt;. The model
is the plain data and the store is the container of data. We'll be able to add
models to the store or to query the store to get stored models. We store our
models as key-value where the key is a &lt;code&gt;String&lt;/code&gt; and the value is a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;,
so every model should be serializable.&lt;/p&gt;
&lt;p&gt;This serialization is the bigger overhead added. We need to do this because
we need to be able to store this in the LMDB database. Every request will create
a copy of the object in the database, so we're not using the same data. This can
be tuned to use pointers to the real data, but to do that we'll need to use
&lt;em&gt;unsafe&lt;/em&gt; code and I think that the performance that we'll get with this doesn't
deserve the complexity that this will add.&lt;/p&gt;
&lt;p&gt;By default, the &lt;code&gt;Model&lt;/code&gt; trait has two methods &lt;code&gt;fromb&lt;/code&gt; and &lt;code&gt;tob&lt;/code&gt; to serialize
and deserialize using &lt;a href="https://crates.io/crates/bincode"&gt;bincode&lt;/a&gt;, so any struct that implements the &lt;code&gt;Model&lt;/code&gt;
trait and doesn't reimplement these two methods should implement &lt;code&gt;Serialize&lt;/code&gt;
and &lt;code&gt;Deserialize&lt;/code&gt; from &lt;a href="https://crates.io/crates/serde_derive"&gt;serde&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The signal system is an addition to be able to register callbacks to &lt;strong&gt;keys&lt;/strong&gt;
modifications in the &lt;strong&gt;store&lt;/strong&gt;, so we can do something when a new objects is
added, modified or deleted from the store. The signaler is optional and we
should use it in a explicit way.&lt;/p&gt;
&lt;h2&gt;How to use it&lt;/h2&gt;
&lt;p&gt;First of all, you should define your data model, the struct that you want to
be able to store in the database:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#[derive(Serialize, Deserialize, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&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;In this example we'll define a struct called &lt;strong&gt;A&lt;/strong&gt; with two attributes, &lt;strong&gt;p1&lt;/strong&gt;,
a &lt;code&gt;String&lt;/code&gt;, and &lt;strong&gt;p2&lt;/strong&gt;, an &lt;code&gt;u32&lt;/code&gt;. We derive &lt;code&gt;Serialize&lt;/code&gt; and &lt;code&gt;Deserialize&lt;/code&gt;
because we're using the default &lt;code&gt;fromb&lt;/code&gt; and &lt;code&gt;tob&lt;/code&gt; from the &lt;code&gt;Model&lt;/code&gt; trait.&lt;/p&gt;
&lt;p&gt;Then we need to implement the &lt;code&gt;Model&lt;/code&gt; trait:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}:{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&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;We only reimplement the &lt;code&gt;key&lt;/code&gt; method to build a key for every instance of &lt;code&gt;A&lt;/code&gt;.
In this case our key will be the &lt;code&gt;String&lt;/code&gt; followed by the number, so for example
if we've something like &lt;code&gt;let a = A { p1: "myk", p2: 42 };&lt;/code&gt; the key will be
"myk:42".&lt;/p&gt;
&lt;p&gt;Then, to use this we need to have a &lt;code&gt;Store&lt;/code&gt;, in this example, we'll use the
LMDB store that's the struct &lt;code&gt;Cache&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// initializing the cache. This str will be the fs persistence path&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/tmp/mydb.lmdb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We pass the path to the filesystem where we want to persist the cache as the
first argument, in this example we'll persist to "/tmp/mydb.lmdb". When we
ran the program for the first time a directory will be created there. The next
time, that cache will be used with the information from the previous execution.&lt;/p&gt;
&lt;p&gt;Then, with this &lt;code&gt;cache&lt;/code&gt; object we can instantiate an &lt;code&gt;A&lt;/code&gt; object and store
in the &lt;code&gt;cache&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// create a new *object* and storing in the cache&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&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;cache&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert!&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="n"&gt;is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;store&lt;/code&gt; method will serialize the object and store a copy of that in the
cache.&lt;/p&gt;
&lt;p&gt;After the store, we can query for this object from other process, using the
same lmdb path, or from the same process using the cache:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// querying the cache by key and getting a new *instance*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello:42&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll get a copy of the original one.&lt;/p&gt;
&lt;p&gt;This is the full example:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#[macro_use]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;serde_derive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Serialize, Deserialize, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}:{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&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;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// initializing the cache. This str will be the fs persistence path&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/tmp/mydb.lmdb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// create a new *object* and storing in the cache&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&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;cache&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert!&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="n"&gt;is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// querying the cache by key and getting a new *instance*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello:42&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&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;Iterations&lt;/h3&gt;
&lt;p&gt;When we store objects with the same key prefix we can iterate over all of
the objects, because we don't know the full key of all objects.&lt;/p&gt;
&lt;p&gt;Currently there's two ways to iterate over all objects with the same prefix
in a &lt;code&gt;Store&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;all&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the simpler way, calling the &lt;code&gt;all&lt;/code&gt; method we'll receive a &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; so
we've all the objects in a vector.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hellows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hellows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hellow: {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This has a little problem, because if we've a lot of objects, this will use a
lot of memory for the vector and we'll be iterating over all objects twice. To
solve this problems, the &lt;code&gt;iter&lt;/code&gt; method was created.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;iter&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;iter&lt;/code&gt; method provides a way to call a closure for every object with this
prefix in the key. This closure should return a &lt;code&gt;Continue(bool)&lt;/code&gt; that will
indicates if we should continue iterating of if we should stop the iteration
here.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;iter&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;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hellow: {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Continue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using the &lt;code&gt;Continue&lt;/code&gt; we can avoid to iterate over all the objects, for example
if we're searching for one concrete object.&lt;/p&gt;
&lt;p&gt;We're copying every object, but the &lt;code&gt;iter&lt;/code&gt; method is better than the &lt;code&gt;all&lt;/code&gt;,
because if we don't copy or move the object from the closure, this copy only
live in the closure scope, so we'll use less memory and also, we only iterate
one. If we use &lt;code&gt;all&lt;/code&gt;, we'll iterate over all objects with that prefix to build
the vector so if we iterate over that vector another time this will cost more
than the &lt;code&gt;iter&lt;/code&gt; version.&lt;/p&gt;
&lt;h2&gt;Signal system&lt;/h2&gt;
&lt;p&gt;As I said before, the signal system provide us a way to register callbacks
to key modifications. The signal system is independent of the &lt;code&gt;Model&lt;/code&gt; and
&lt;code&gt;Store&lt;/code&gt; and can be used independently:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Signaler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SignalerAsync&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SignalerAsync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal_loop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&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="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// one thread for receive signals&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_sig&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// waiting for threads to finish&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// one thread for emit signals&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2:3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// waiting for threads to finish&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ten_millis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;from_millis&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="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ten_millis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&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;In this example we're creating a &lt;code&gt;SignalerAsync&lt;/code&gt; that can &lt;code&gt;emit&lt;/code&gt; signal and
we can &lt;code&gt;subscribe&lt;/code&gt; a callback to any signal. The &lt;code&gt;sig.signal_loop();&lt;/code&gt; init the
signal loop thread, that wait for signals and call any subscribed callback when
a signal comes.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_sig&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&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 subscribe a callback to the signaler. The signaler can be cloned and the list
of callbacks will be the same, if you emit a signal in a clone and subscribe in
other clone, that signal will trigger the callback.&lt;/p&gt;
&lt;p&gt;Then we're emiting some signals:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2:3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All of this three signals will trigger the previous callback because the
subscription works as a &lt;em&gt;signal starts with&lt;/em&gt;. This allow us to subscribe to
all new room messages insertion if we follow the previous described keys,
subscribing to "msg:roomid" and if we only want to register a callback to
be called only when one message is updated we can subscribe to
"msg:roomid:msgid" and this callback won't be triggered for other messages.&lt;/p&gt;
&lt;p&gt;The callback should be a &lt;code&gt;Box&amp;lt;Fn(signal)&amp;gt;&lt;/code&gt; where signal is the following
struct:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#[derive(Clone, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SigType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Clone, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Currently only &lt;code&gt;Update&lt;/code&gt; and &lt;code&gt;Delete&lt;/code&gt; signal types are supported.&lt;/p&gt;
&lt;h3&gt;Signaler in gtk main loop&lt;/h3&gt;
&lt;p&gt;All the UI operations in a gtk app should be executed in the gtk main loop so
we can't use the &lt;code&gt;SignalerAsync&lt;/code&gt; in a gtk app, because this signaler creates
one thread for the callbacks so all callbacks should implement the &lt;code&gt;Send&lt;/code&gt; trait
and if we want to modify, for example, a &lt;code&gt;gtk::Label&lt;/code&gt; in a callback, that
callback won't implement &lt;code&gt;Send&lt;/code&gt; because &lt;code&gt;gtk::Label&lt;/code&gt; can't be send between
threads safely.&lt;/p&gt;
&lt;p&gt;To solve this problem, I've added the &lt;code&gt;SignalerSync&lt;/code&gt;. That doesn't launch any
threads and where all operations runs in the same thread, even the callback.
This is a problem if one of your callbacks locks the thread, because this will
lock your interface in a gtk app, so any callback in the sync signaler should
be non blocking.&lt;/p&gt;
&lt;p&gt;This signaler should be used in a different way, so we should call from time
to time to the &lt;code&gt;signal_loop_sync&lt;/code&gt; method, that will check for new signals and
will trigger any subscribed callback. This signaler doesn't have a &lt;code&gt;signal_loop&lt;/code&gt;
because we should do the loop in our thread.&lt;/p&gt;
&lt;p&gt;This is an example of how to run the signaler loop inside a gtk app:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SignalerSync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;timeout_add&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="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Continue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal_loop_sync&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// We can subscribe callbacks using the sig here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example code we're registering a timeout callback, every 50ms this
closure will be called, from the gtk main thread, and the &lt;code&gt;signal_loop_sync&lt;/code&gt;
will check for signals and call the needed callbacks.&lt;/p&gt;
&lt;p&gt;This method returns a &lt;code&gt;bool&lt;/code&gt; that's false when the signaler stops. You can
stop the signaler calling the &lt;code&gt;stop&lt;/code&gt; method.&lt;/p&gt;
&lt;h2&gt;Point of extension&lt;/h2&gt;
&lt;p&gt;I've tried to make this crate generic to be able to extend in the future and
provide other kind of cache that can be used changing little code in the apps
that uses &lt;strong&gt;mdl&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is the main reason to use traits to implement the store, so the first point
of extension is to add more cache systems, we're currently two, the LMDB and
the BTreeMap, but it would be easy to add more key-value storages, like
memcached, &lt;a href="https://crates.io/crates/unqlite"&gt;unqlie&lt;/a&gt;, mongodb, redis, couchdb, etc.&lt;/p&gt;
&lt;p&gt;The signaler is really simple, so maybe we can start to think about new
signalers that uses &lt;code&gt;Futures&lt;/code&gt; and other kind of callbacks registration.&lt;/p&gt;
&lt;p&gt;As I said before, mdl does a copy of the data on every write and on every read,
so it could be cool to explore the implication of these copies in the
performance and try to find methods to reduce this overhead.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 03 Aug 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-08-03:/mdl.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>mdl</category><category>fractal</category></item><item><title>GUADEC 2018</title><link>https://danigm.net/guadec2018.html</link><description>&lt;h2&gt;GUADEC 2018&lt;/h2&gt;
&lt;p&gt;GUADEC is the &lt;strong&gt;GNOME Users And Developers European Conference&lt;/strong&gt;, is an annual
conference that take place in Europe, and this year was in Spain, so I should go.
I've became a foundation member this year and I've two Google Summer of Code students
from GNOME organization working on Fractal, so this year GUADEC was an important
one for me.&lt;/p&gt;
&lt;p&gt;This year &lt;a href="https://2018.guadec.org/"&gt;GUADEC&lt;/a&gt; was in Almería and I was there for the main days. I can't
stay there for the full GUADEC because I'm using my holidays, so I stay in Almería
from Thursday 5th to Sunday 8th and then I continue to Murcia to enjoy my free
time in the beach.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/almeria.jpg"&gt;
        &lt;img src="/pictures/almeria.jpg" alt="almeria" /&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Not my first GUADEC&lt;/h2&gt;
&lt;p&gt;Until the end of 2017 I've not been GNOME foundation member, but I've been
doing small contributions to GNOME since 2010.&lt;/p&gt;
&lt;p&gt;My first GUADEC was the Gran Canaria Desktop Summit, a big event that joined in
one city the GUADEC and Akademy so there was a lot of desktop developers, from
GNOME, KDE and other open source projects. I was there with some friends, I was
working with some GNOME developers, in the &lt;a href="https://en.wikipedia.org/wiki/Guadalinex"&gt;guadalinex&lt;/a&gt; project, the year before
so I started to know the GNOME community.&lt;/p&gt;
&lt;p&gt;The next year I was in the Hague GUADEC. This year I was there as a developer,
I was working in the Evince accessibility support so my employer paid the travel
and all costs.&lt;/p&gt;
&lt;p&gt;After that, I don't have more paid work related to GNOME, and started to work with
Qt, so I started to go to &lt;a href="https://en.wikipedia.org/wiki/Akademy"&gt;Akademy&lt;/a&gt;, but I don't leave the GNOME community,
I was always a GNOME user and was there doing small contributions.&lt;/p&gt;
&lt;p&gt;My last GUADEC was the 2012, in &lt;strong&gt;A Coruña&lt;/strong&gt;, Spain again. Indeed I'm in the
wikipedia GUADEC article photo, from that year, try to find me there.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/guadec-2012.jpg"&gt;
        &lt;img src="/pictures/guadec-2012.jpg" alt="guadec-2012" /&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Almería&lt;/h2&gt;
&lt;p&gt;This year GNOME was a great event, with good social events. We've a beach party
with a big paella and the famous &lt;em&gt;Ice Cream Death Match&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We've a good time in the Alcazaba of Almería, with a great performance done by
the GNOME crew.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/alcazaba-gnome.jpg"&gt;
        &lt;img src="/pictures/alcazaba-gnome.jpg" alt="alcazaba gnome" /&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;Then we've a flamenco show in a great place with drinks and some food.&lt;/p&gt;
&lt;h2&gt;GNOME&lt;/h2&gt;
&lt;p&gt;As I said before, this is not my first GUADEC, but this one is an important one
for me, because Fractal is becoming important and this year I've been working
with a lot of great people and it was good to meet them in the GUADEC.&lt;/p&gt;
&lt;p&gt;This year I've been in the Madrid Rust hackfest and in the Strasbourg Fractal
hackfest, unfortunately I can't stay there for the working days because there was
interesing BoFs about Rust and other technologies.&lt;/p&gt;
&lt;p&gt;It was great to meet again with Julian and Tobias, and to meet in person other
people that I've been working with, like Carlos Soriano and Alberto Fanjul.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/guadec-2018.jpg"&gt;
        &lt;img src="/pictures/guadec-2018.jpg" alt="guadec 2018 group photo" /&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;The most important thing from this GUADEC, for me, is the growth plan for GNOME,
the new board of directors want to grow the GNOME projects, attracting more
money and more developers.&lt;/p&gt;
&lt;p&gt;The GNOME project starts to loose interest and developers when the mobile app
ecosystem starts to grow, but it seems that now the interest in this great project
is increasing again, thanks to a lot of innovative projects like flatpak,
gnome-builder, librem5, etc and a lot of great changes like the gitlab migration.
The gnome Board of Directors is doing a great work too, increasing the money
spend in hackfest, to make it easy for developers to work in the gnome projects.&lt;/p&gt;
&lt;p&gt;I've no doubt that GNOME will grow during the next year in developers and
importance. I've been around a lot of time, doing a little in my free time, but
I'll try to spend more time in GNOME and I'll try to find a way to get payed
for it :D&lt;/p&gt;
&lt;p&gt;See you in the 2019 GUADEC.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 13 Jul 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-07-13:/guadec2018.html</guid><category>blog</category><category>gnome</category><category>guadec</category><category>almeria</category></item><item><title>LMDB: Cache database in memory</title><link>https://danigm.net/lmdb.html</link><description>&lt;p&gt;Some days ago I was in a meeting talking about the E2E implementation for
Fractal, to know the current implementation state and to talk with other
developers. This meeting was promoted by &lt;a href="https://puri.sm/"&gt;Puri.sm&lt;/a&gt; people, because they
want to use Fractal for the Librem5 phone and they want to have E2E. There's
people working in the E2E and I think we can have this on Fractal at the end
of the year, but this is not what I want to talk about today.&lt;/p&gt;
&lt;p&gt;In this meeting there was Fractal developers but we've also other people,
like the &lt;a href="https://github.com/mujx/nheko"&gt;nheko&lt;/a&gt; developer, mujx (nheko is a Qt matrix client). During the
meeting, mujx ask us about the cache storage that we're using, because for the
E2E is too important to don't lose any key, because that'll be catastrophic,
you won't be able to read room messages. So it's important to have a
transactional database storage for this information. They are using &lt;a href="http://www.lmdb.tech/doc/"&gt;LMDB&lt;/a&gt;,
I didn't know nothing about LMDB so after this meeting I start to about it.&lt;/p&gt;
&lt;h2&gt;LMDB&lt;/h2&gt;
&lt;p&gt;Lightning Memory-Mapped Database Manager (LMDB) is a key-value database, it's
memory mapped so it's fast, and also uses filesystem storage so we've
persistence. This database has transactions so it's safe to read/write from
different threads or process.&lt;/p&gt;
&lt;p&gt;In Fractal we're using a simple json file for cache, but this doesn't support
transactions and if the app crash or if something bad happens, we can lose data.
This method is simple, but is slow and insecure, so using LMDB will improve
Fractal in several ways.&lt;/p&gt;
&lt;p&gt;But LMDB is in memory and in Fractal we've a lot of interface code sharing the
app state, so we're passing the state between threads with copies and complex
data sharing. This can simplify the interface code because using LMDB for the
application global state will make this state accesible from different threads.&lt;/p&gt;
&lt;h2&gt;Testing LMDB&lt;/h2&gt;
&lt;p&gt;Fractal is written in Rust so I want to write some tests before start to use
this on Fractal. There's a simple &lt;a href="https://crates.io/crates/lmdb"&gt;LMDB rust crate&lt;/a&gt;, and I've been writting
an &lt;a href="https://gitlab.gnome.org/danigm/lmdb-rs-tests"&gt;example lib&lt;/a&gt; to test it.&lt;/p&gt;
&lt;p&gt;Basically what I've done is to write a simple trait that you can implement for
simple Rust structs so that struct can be stored and recover from the cache
with a simple method:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="nx"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Cacheable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TESTDB&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;b:{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&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 struct should be serializable/deserializable with serde because in this
example I'm using &lt;a href="https://crates.io/crates/bincode"&gt;bincode&lt;/a&gt; to convert structs to [u8].&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Cacheable&lt;/code&gt; trait only have two required methods, the db and the key. The
db is the db name to use to store this struct instances and the key is the
key to use when storing a concrete instance.&lt;/p&gt;
&lt;p&gt;With this, we can store in the cache and query from the cache, using the key:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;format&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{}-basic&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;new&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;unwrap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;complex&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="cp"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;store&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;is_ok&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;b:1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;unwrap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;len&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;One string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;to_string&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Second string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;to_string&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;store&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;b:1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;unwrap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;len&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="cp"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;..&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;One string&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is thread safe, so we can read from the cache from different threads and
we'll always get the last version in the database.&lt;/p&gt;
&lt;p&gt;LMDB is a key-value database, so we don't have relations. This is not a real
problem for us because we can model the relations in the keys, for example, we
can store Room messages with keys like this "message:ROOMID:messageID" and then
we can iterate over all objects with this prefix: "message:ROOMID" that will
give us all room messages. Something like this should work:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;format&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;message:{}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;iter&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fetched&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;database,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;what&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;Continue(true)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;LMDB in Fractal data model&lt;/h2&gt;
&lt;p&gt;We're thinking about moving the Fractal data model from the AppOp struct to
a new crate, independent of the UI, to simplify the UI code and to be able
to use the same data model from different UIs (we wan't to split fractal in
two different apps), &lt;a href="https://blogs.gnome.org/jsparber/2018/06/17/refactor-backend-and-ui/"&gt;Julian wrote about this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think that we can use the LMDB cache to store this new app state that we
want to have and this will simplify a lot our code, because we will share the
same state and this state will be persisted in filesystem.&lt;/p&gt;
&lt;p&gt;I'll start to write a new crate for Fractal to start to move all the app state
to this new crate. I think I can write a generic tool to simplify the LMDB use,
maybe I'll publish another crate in crates.io and use that in Fractal, but I
need to think a little more about the pattern to follow.&lt;/p&gt;
&lt;p&gt;I write a lot of web code in my day to day work, and I've been working with
react+redux. This LMDB cache thing in Fractal reminds me a lot to the redux
store and I want to follow a similar pattern so we can have only an app state
and only a way to update this state.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 30 Jun 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-06-30:/lmdb.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>lmdb</category><category>cache</category><category>fractal</category></item><item><title>Stickers in Riot</title><link>https://danigm.net/stickers-in-riot.html</link><description>&lt;p&gt;Yesterday I read a &lt;a href="https://medium.com/@RiotChat/stickers-are-here-introducing-riot-im-0-15-for-web-desktop-284c32b93acc"&gt;blog post&lt;/a&gt; about the new Riot.im Stickers. This is
not a matrix.org feature, it's implemented as a widget, when you send an
sticker you're sending a new event with the type "m.sticker", which is
similar to the "m.image" event.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/stickers.gif" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;The matrix.org protocol is flexible so this is a good example of how to add
new features to the clients that uses matrix without the need to change the
protocol.&lt;/p&gt;
&lt;p&gt;This is not a core feature because you can send images, but I think this is
great and add a simple way to show reactions for the users, so as I was
reading I thought that we can add this to &lt;a href="https://wiki.gnome.org/Apps/Fractal"&gt;Fractal&lt;/a&gt;, so I started to
read how we can add support for this.&lt;/p&gt;
&lt;h2&gt;Reading the doc&lt;/h2&gt;
&lt;p&gt;The first thing to implement a feature is to read the specifications or the
technical documentation so we can know what is needed... But there's no
&lt;a href="https://github.com/matrix-org/matrix-doc/issues/1236"&gt;documentation yet about Stickers or widgets yet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a problem because we can't implement a feature if we don't know
what we should do. But free software give us a great opportunity when this
happens, we've the &lt;a href="https://github.com/vector-im/riot-web"&gt;Riot&lt;/a&gt; source code so we can look at the code and
learn what they are doing.&lt;/p&gt;
&lt;h2&gt;Reading the code&lt;/h2&gt;
&lt;p&gt;Riot web is a javascript application that uses AJAX to communicate with
different server APIs so the first thing that I did to start to understand
the stickers thing was to open the firefox debugger and view how riot is
communicating with the server.&lt;/p&gt;
&lt;p&gt;From here I've learned that for stickers riot is asking to the
scalar.vector.im server. But I don't understand the whole thing with the
requests because riot does a lot of request to different APIs and I can't
isolate the stickers thing easily.&lt;/p&gt;
&lt;p&gt;To fill my understanding gap I go to the &lt;a href="https://github.com/matrix-org/matrix-js-sdk"&gt;matrix-js-sdk&lt;/a&gt; and
&lt;a href="https://github.com/matrix-org/matrix-react-sdk"&gt;matrix-react-sdk&lt;/a&gt; and I did a quick grep to the source code looking for
the API calls that I've view in firefox. With this I can understand the
full stickers process.&lt;/p&gt;
&lt;h2&gt;Writing an example&lt;/h2&gt;
&lt;p&gt;To say that I know how this is working, it's not enough the code reading.
To make sure that I've understood the whole process I need to write a
simple program that does all the process and then I can say that I
understand this.&lt;/p&gt;
&lt;p&gt;So I started to write a &lt;a href="https://github.com/danigm/matrix-stickers-example"&gt;simple python script&lt;/a&gt; using requests. This
simple script does the request to the server and list all the stickers json
so I can say that I'm able to communicate with the API.&lt;/p&gt;
&lt;h2&gt;Stickers in Fractal&lt;/h2&gt;
&lt;p&gt;After this small research I'm able to implement an initial sticker support
for Fractal. I'll try to add a simple way to show and use stickers and a
way to render stickers in the messages history.&lt;/p&gt;
&lt;p&gt;If there's no secret problems we'll have a basic stickers support in
Fractal soon.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 19 May 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-05-19:/stickers-in-riot.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>fractal</category></item><item><title>Fractal Hackfest, Strasbourg (day 2)</title><link>https://danigm.net/fractal-hackfest2.html</link><description>&lt;p&gt;The second day of Fractal hackfest was really productive, we talk about a
lot of topics and takes some important decisions.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-hackfest2.png" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;E2E Encryption&lt;/h2&gt;
&lt;p&gt;The encryption is a needed feature but encryption is hard to do in rooms.
Matrix uses public-key cryptography, for rooms they are using &lt;a href="https://git.matrix.org/git/olm/about/docs/megolm.rst"&gt;Megolm&lt;/a&gt;,
that's a protocol to exchange encrypted messages with more than one and
share that message keys in a one-to-one secure communication.&lt;/p&gt;
&lt;p&gt;I don't know a lot about this E2E because for me it's more important to
have the client working with a basic functionality before the encryption.
So you should read the official doc because maybe this that I'm writing
here is completely wrong.&lt;/p&gt;
&lt;p&gt;To do all this E2E key sharing, client side encryption and communication,
Riot has three different implementations of the &lt;em&gt;same&lt;/em&gt; lib, so they have
this code in the JavaScript SDK, the same ported to iOS version in
ObjectiveC and the same ported to Android in Java. Below this lib there's
the libolm that does the real encryption.&lt;/p&gt;
&lt;p&gt;For the future it should be a good solution to have only one lib that does
the encryption thing, so we need to push in that direction and maybe
matrix.org and Purism can pay for that work to be done.&lt;/p&gt;
&lt;p&gt;From Fractal, we've two options or wait until we've the official E2E
C/C++-lib or something, or try to port the iOS lib to Rust or C to use in
our codebase. I think that the best solution is the first one, in any case,
we'll need to wait some time to view E2E in Fractal.&lt;/p&gt;
&lt;h2&gt;Calls&lt;/h2&gt;
&lt;p&gt;Calls are easy compared to the Encryption thing, the &lt;a href="https://matrix.org/docs/spec/client_server/r0.3.0.html#voice-over-ip"&gt;protocol is well documented&lt;/a&gt;
and it's really simple. The hard part here is that the call is implemented
using the webRTC and that is a web protocol... But it seems that there's
something done in this case with GStreamer working with webRTC.&lt;/p&gt;
&lt;h2&gt;Multiple accounts&lt;/h2&gt;
&lt;p&gt;We was talking yesterday about the multiple accounts problem, if we want to
support and in that case what solution we should take.&lt;/p&gt;
&lt;p&gt;We've two possible solutions, one is to support multiple accounts at the
same time and a way to change between accounts, and the other one is to
open a window for each account.&lt;/p&gt;
&lt;p&gt;At the end we think that the best solution could be to open a separated
window for each account so if you've multiple accounts, at first you should
choose what account you want to open and then from the menu you can open a
new &lt;em&gt;window&lt;/em&gt; and go to the account selection menu again.&lt;/p&gt;
&lt;p&gt;We need to work in the design of this feature and it's not confirmed but I
think that this was the best solution, because it's simple and powerful.&lt;/p&gt;
&lt;h2&gt;Communities&lt;/h2&gt;
&lt;p&gt;Communities are something really new in the Matrix.org protocol and it's
not well documented yet and there's some functionality that is not
implemented so we decide to wait until this functionality is more mature to
view how we can integrate this in Fractal.&lt;/p&gt;
&lt;p&gt;Communities today are a group of rooms and a group of people and a full
html description to show in the community &lt;em&gt;page&lt;/em&gt;. There's other features
like show a &lt;em&gt;label&lt;/em&gt; for each member of the community or group, so I think
these features are cool. We'll need to support some parts of the
communities protocol soon.&lt;/p&gt;
&lt;h2&gt;Google Summer of Code&lt;/h2&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-hackfest1.png" width="100%" /&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;As I said this year we've two students working on Fractal thanks to Google,
as part of the &lt;a href="https://summerofcode.withgoogle.com/organizations/5900447454330880/"&gt;GNOME organization&lt;/a&gt;, and both students comes to the
Hackfest so thank you Julian and Eisha for coming and for the work done
before the GSoC and for sure for the work you'll be doing during the
summer.&lt;/p&gt;
&lt;p&gt;We're talking about how we'll be organizing, I'm the mentor of both but
we'll have the help from Tobias Bernard and from Alexandre Franke so we'll
have a summer hacking group to improve Fractal a lot.&lt;/p&gt;
&lt;p&gt;We'll have a weekly meeting to talk about what we're doing and what to do
in the next week.&lt;/p&gt;
&lt;p&gt;For this first week we'll start with two needed features. Julian will be
working in the user preference dialog and Eisha will start to work in the
internationalization (i18n) because it's not easy to use gettext in rust
and we want to translate the app to make it more accessible for everyone in
the world.&lt;/p&gt;
&lt;h2&gt;Hacking a bit&lt;/h2&gt;
&lt;p&gt;In the meantime, between functionality discussion I was working in a way to
use Fractal without SecretService, so if you're using Fractal outside GNOME
or KDE you can configure it to store password and token in a plain text.
We shouldn't store passwords in a clear text file, but I think that this
will simplify the day for many people that wants to use Fractal but don't
want to write the user and password every time.&lt;/p&gt;
&lt;p&gt;This is not finished yet, but we'll have this very soon, and with this it's
possible to implement other password &amp;amp; token storage services and make it
configurable, so maybe in the future someone implements the storage for
MacOS or something.&lt;/p&gt;
&lt;h2&gt;Holidays&lt;/h2&gt;
&lt;p&gt;This was my last day in the hackfest, I've the travel back to Spain the
Sunday but I'll do some tourism here so I need to leave the people, they
will continue working the Saturday and the Sunday.&lt;/p&gt;
&lt;p&gt;This have been the first Fractal hackfest and it was a great experience, to
meet other people working on it and to talk about the future of the
application. It's really cool that with less than a year project we've now
a big community, so thank you all form coming and see you on GUADEC.&lt;/p&gt;
&lt;p&gt;There's a lot of work to do, but we're lucky because we're two students
working in Fractal this summer and the people from &lt;a href="https://matrix.org/blog/2018/05/11/this-week-in-matrix-2018-05-11/"&gt;Matrix.org&lt;/a&gt; and
&lt;a href="https://puri.sm/posts/librem5-progress-report-11/"&gt;Purism&lt;/a&gt; are working with us, so thank you all.&lt;/p&gt;
&lt;p&gt;I want to thank again the GNOME foundation and GNOME people for make this
possible and to my company &lt;a href="https://wadobo.com"&gt;Wadobo&lt;/a&gt;, for let me spend some time working
on this great project.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;a href="https://wadobo.com"&gt;
        &lt;img src="/pictures/wadobo-mini.png" /&gt;
    &lt;/a&gt;
&lt;/center&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 12 May 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-05-12:/fractal-hackfest2.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>fractal</category><category>wadobo</category></item><item><title>Fractal Hackfest, Strasbourg (day 1)</title><link>https://danigm.net/fractal-hackfest1.html</link><description>&lt;p&gt;Yesterday was the first day in the first &lt;a href="https://wiki.gnome.org/Hackfests/Fractal2018"&gt;Fractal Hackfest&lt;/a&gt;. I'll try to
write an small blog post every day to share the development with the world.&lt;/p&gt;
&lt;p&gt;My travel to Strasbourg was not an easy travel because I've to take two
flights to get here from Málaga so a long day travelling.&lt;/p&gt;
&lt;p&gt;I met with Mathew from Matrix.org at the London airport because we took the
same flight to here and it was really cool to meet him in person and we
talk a little about the current Matrix situation.&lt;/p&gt;
&lt;p&gt;I've met the other Fractal people and collaborators at the event, and it's
great that people from Purism, Matrix, Gnome and the two GSoC students come
here to work together in this great application.&lt;/p&gt;
&lt;h2&gt;Barbecue and Banquet&lt;/h2&gt;
&lt;p&gt;We've spend almost all the day talking about the different uses cases for
the messaging problem and Tobias has come with a proposal for split the
current application in two because the use case is totally different.&lt;/p&gt;
&lt;p&gt;We've the first case when you use the app to talk to family, friends and
small groups of people where all conversations are private and you want to
read almost every message there, we call that situation the "Barbecue".&lt;/p&gt;
&lt;p&gt;The second case is the one where you use the messaging app to talk in a
public or private room with a lot of people, where you are receiving a lot
of messages but you don't want to follow the full conversation you'll talk
with someone or read a topic conversation, we call this one the "Banquet".&lt;/p&gt;
&lt;p&gt;Currently Matrix clients are more oriented to the Banquet use case so
there's a lot of people and communities using it for this. The idea behind
this split is to provide two apps to the user to make both cases possible.&lt;/p&gt;
&lt;p&gt;We need to work on this idea but it seems that is the correct way to go.&lt;/p&gt;
&lt;h2&gt;Initial Syn speed up&lt;/h2&gt;
&lt;p&gt;At the afternoon. We've reviewing the current initial sync filter and
detect some points to speedup.&lt;/p&gt;
&lt;p&gt;I changed the filter and the initial sync goes a lot faster. I've removed
the member events from the initial sync so there's a lot of less
information to retrieve from the server.&lt;/p&gt;
&lt;p&gt;This little change breaks the one to one room name and default avatars
because that name is calculated using the members in the room so I need to
fix that making the name calculation a backend process. So now if a room
has no name, We'll ask for the room member list and when we've that we'll
calculate the room name with the same algorithm, if there're only two
people, we'll use the other person username, if there's three people we'll
use "one and another" and if there're more we'll use "one and others".&lt;/p&gt;
&lt;h2&gt;Gnome dinner&lt;/h2&gt;
&lt;p&gt;We've a good dinner sponsored by the gnome foundation. Thank you so much.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 11 May 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-05-11:/fractal-hackfest1.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>fractal</category><category>wadobo</category></item><item><title>GNOME 💙 Rust Hackfest in Madrid</title><link>https://danigm.net/rust-gnome.html</link><description>&lt;p&gt;The last week was the &lt;a href="https://wiki.gnome.org/Hackfests/Rust2018#Reports"&gt;GNOME 💙 Rust hackfest&lt;/a&gt; in Madrid. I was there,
only for the first two days, but was a great experience to meet the people
working with Rust in GNOME a great community with a lot of talented people.&lt;/p&gt;
&lt;p&gt;The event was in the &lt;a href="https://www.openshine.com/"&gt;OpenShine&lt;/a&gt; office, that was a great place and the
OpenShine people was very friendly too so thank you very much to OpenShine
for support this kind of events.&lt;/p&gt;
&lt;h2&gt;What I did during the Hackfest&lt;/h2&gt;
&lt;p&gt;I arrive at 10:30, more or less, and there are some people there, but two
or three comes after the lunch, so the first morning we started to work,
each one in their project.&lt;/p&gt;
&lt;p&gt;I've some patches for &lt;a href="https://gitlab.gnome.org/World/fractal"&gt;Fractal&lt;/a&gt; to review and I was working on the glade
file splitting. After that I downloaded the &lt;a href="https://gitlab.gnome.org/federico/gnome-class"&gt;gnome-class&lt;/a&gt; code and
started to read and try to understand the code because I want to help in
the gnome-class development during the Hackfest.&lt;/p&gt;
&lt;p&gt;After the lunch, the missing people from the morning was there so we did an
official "Hackfest presentation" everyone did a little presentation about
his work and what want to do during the event.&lt;/p&gt;
&lt;p&gt;Gnome-class was the main project to join GNOME and Rust, and it's still in
a early alpha stage so Federico give us a little talk about what's
gnome-class and its parts.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/madrid-whiteboard.jpg"&gt;
        &lt;img src="/pictures/madrid-whiteboard.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;Gnome-class&lt;/h2&gt;
&lt;p&gt;Gnome-class is basically a &lt;em&gt;compiler&lt;/em&gt; that translate custom syntax to
&lt;em&gt;Gobject&lt;/em&gt; binary compatible Rust code. Using the &lt;a href="https://doc.rust-lang.org/unstable-book/language-features/proc-macro.html"&gt;proc-macro&lt;/a&gt;,
gnome-class parses a &lt;em&gt;Gobject&lt;/em&gt; like declaration and creates all the Rust
code needed to use the &lt;em&gt;Glib&lt;/em&gt; so we can have binary code that can be called
from &lt;em&gt;C&lt;/em&gt; or from &lt;em&gt;Rust&lt;/em&gt;. With this we can do Object Oriented programming in
Rust using the &lt;em&gt;Gobject&lt;/em&gt; library and types.&lt;/p&gt;
&lt;p&gt;So we've &lt;a href="https://federico.pages.gitlab.gnome.org/gnome-class/"&gt;three parts in gnome-class&lt;/a&gt;, the parser, the High-level
Internal Representation (HIR) and the code generation.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;gobject_gen&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nl"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;u32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;add&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;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&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="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&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;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&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="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;Counter has value: {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;assert_eq&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;Counter has value: {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is Rust code with gnome-class, here we're creating a new &lt;em&gt;Class&lt;/em&gt;
called &lt;code&gt;Counter&lt;/code&gt; with two public methods, then we've a test that use this.
Behind this code, gnome-class generates a lot of ugly code with pointers
and so to make all of this C-Compatible and generate all the needed binary
using the &lt;em&gt;GObject&lt;/em&gt; data scheme.&lt;/p&gt;
&lt;h2&gt;My work in gnome-class&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.gnome.org/gobject/stable/gobject-properties.html"&gt;Properties&lt;/a&gt; declaration was not supported in gnome-class so took that
&lt;a href="https://gitlab.gnome.org/federico/gnome-class/issues/2"&gt;task&lt;/a&gt;. There was a &lt;a href="https://gitlab.gnome.org/federico/gnome-class/blob/master/gobject-notes/syntax.md"&gt;proposed syntax&lt;/a&gt; and I started to work in the
parser to try to support that syntax and convert all the relevant
information into a Rust struct in the HIR tree.&lt;/p&gt;
&lt;p&gt;To parse that I needed to learn &lt;a href="https://crates.io/crates/syn"&gt;syn&lt;/a&gt; that's a parser based in
&lt;a href="https://crates.io/crates/nom"&gt;nom&lt;/a&gt;. That was not easy, but there's a lot of code in gnome-class so I
can read and learn from that and at the end of the day I had a working
properties parser.&lt;/p&gt;
&lt;p&gt;The second day I continue with my work and started to generate code. Here I
have more problems because the code generated is too low level and I didn't
now much about the &lt;em&gt;GObject&lt;/em&gt; internals so I was playing around types
conversions.&lt;/p&gt;
&lt;p&gt;I've to go just after the lunch, but during the back to home train trip I
was able to create a &lt;a href="https://gitlab.gnome.org/federico/gnome-class/merge_requests/9"&gt;Merge Request&lt;/a&gt; with all the work I've done.&lt;/p&gt;
&lt;p&gt;I've work to do during the week so I can't continue working on properties
support in gnome-class. But this week I've more time and I've been working
to complete the code generation, so now we've this test working:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#![feature(proc_macro)]&lt;/span&gt;

&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gobject_gen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#[macro_use]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;glib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gobject_gen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;gobject_gen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;gobject_gen&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClassWithProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClassWithProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;p&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="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;p2&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="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyProp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&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="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&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="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Prop2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&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="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_priv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&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="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_props&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ClassWithProps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClassWithProps&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&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="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_property_prop2&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_property_prop2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&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="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_property_prop2&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_property_myprop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_property_myprop&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;58&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;My first GNOME hackfest&lt;/h2&gt;
&lt;p&gt;This was my first GNOME hackfest. I've been in different GUADECs and other
events with GNOME devs, but this was the first time that I travel to work
on a specific &lt;em&gt;project&lt;/em&gt; and not just to meet the people and view talks from
the distance.&lt;/p&gt;
&lt;p&gt;It was a great experience. Rust is really new in the desktop development,
but there's a lot of people working so it's great.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/madrid-food.jpg"&gt;
        &lt;img src="/pictures/madrid-food.jpg" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;The next milestone is the &lt;a href="https://wiki.gnome.org/Hackfests/Fractal2018"&gt;Fractal Hackfest&lt;/a&gt;, we'll be in Strasbourg
four days working.&lt;/p&gt;
&lt;p&gt;I want to thank all the great GNOME community that makes collaboration
so easy, it's a great community.&lt;/p&gt;
&lt;p&gt;Currently all of my work on this is voluntary so I need to thank my
coworkers at &lt;a href="https://wadobo.com"&gt;Wadobo&lt;/a&gt;, because I'm spending some work time in this. I
can do this because my company is great.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;a href="https://wadobo.com"&gt;
        &lt;img src="/pictures/wadobo-mini.png" /&gt;
    &lt;/a&gt;
&lt;/center&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Thu, 26 Apr 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-04-26:/rust-gnome.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>gnome-class</category><category>wadobo</category></item></channel></rss>