Files
pmomusic/Blackboard_HTML/Architecture_pmoconfig_ext.html

1024 lines
161 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>pmoconfig_ext</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@5/github-markdown.min.css">
<script type="module">
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
mermaid.initialize({startOnLoad: true, theme: "default"});
</script>
<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
.back-link {
margin-bottom: 20px;
display: block;
}
pre.mermaid {
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
}
</style>
</head>
<body>
<article class="markdown-body">
<p class="back-link"><a href="index.html">← Retour à l'index</a></p>
<h1 id="pattern-dextension-de-pmoconfigconfig">Pattern dextension de
pmoconfig::Config</h1>
<h2 id="vue-densemble">Vue densemble</h2>
<p>Ce document décrit le pattern architectural utilisé dans PMOMusic
pour étendre la configuration centralisée
(<code>pmoconfig::Config</code>) avec des fonctionnalités spécifiques à
chaque crate.</p>
<h3 id="objectif">Objectif</h3>
<p>Permettre à chaque crate du projet dajouter ses propres méthodes de
configuration sans modifier directement <code>pmoconfig</code>, tout en
maintenant une interface cohérente et type-safe.</p>
<h3 id="principe">Principe</h3>
<p>Chaque crate qui nécessite un accès à la configuration implémente un
<strong>trait dextension</strong> pour <code>pmoconfig::Config</code>.
Ce trait définit des méthodes helpers spécifiques au domaine du crate
(cache, authentification, UPnP, etc.).</p>
<h2 id="architecture-du-pattern">Architecture du pattern</h2>
<h3 id="structure-de-base">Structure de base</h3>
<pre><code>pmoconfig/ # Crate de configuration centralisée
├── Config # Struct principale avec get_value/set_value génériques
└── encryption # Module de chiffrement des mots de passe
pmocrate/ # Crate spécialisé (audio, qobuz, upnp, etc.)
└── config_ext.rs # Trait d&#39;extension pour Config
├── DEFAULT_* # Constantes pour valeurs par défaut
├── XxxConfigExt # Trait d&#39;extension
└── impl # Implémentation du trait pour Config</pre>
<h3 id="flux-de-données">Flux de données</h3>
<pre><code>Application
Trait d&#39;extension spécialisé (QobuzConfigExt, CacheConfigExt, etc.)
pmoconfig::Config (get_value/set_value génériques)
Fichier config.yaml</pre>
<h2 id="implémentation-dun-trait-dextension">Implémentation dun trait
dextension</h2>
<h3 id="structure-du-fichier-config_ext.rs">1. Structure du fichier
config_ext.rs</h3>
<div class="sourceCode" id="cb3"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">//! Extension pour intégrer [fonctionnalité] dans pmoconfig</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="co">//!</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co">//! Ce module fournit le trait `XxxConfigExt` qui permet d&#39;ajouter facilement</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">//! des méthodes de gestion de [fonctionnalité] à pmoconfig::Config.</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">anyhow::</span><span class="dt">Result</span><span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmoconfig::</span>Config<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">serde_yaml::</span>Value<span class="op">;</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Constantes pour valeurs par défaut</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> DEFAULT_XXX_DIR<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span> <span class="op">=</span> <span class="st">&quot;cache_xxx&quot;</span><span class="op">;</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> DEFAULT_XXX_SIZE<span class="op">:</span> <span class="dt">usize</span> <span class="op">=</span> <span class="dv">1000</span><span class="op">;</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="co">/// Trait d&#39;extension pour gérer [fonctionnalité] dans pmoconfig</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="co">/// Ce trait étend `pmoconfig::Config` avec des méthodes spécifiques</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="co">/// à la gestion de [fonctionnalité].</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="co">/// # Exemple</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="co">/// ```rust,ignore</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a><span class="co">/// use pmoconfig::get_config;</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a><span class="co">/// use pmoxxx::XxxConfigExt;</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a><span class="co">/// let config = get_config();</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a><span class="co">/// let value = config.get_xxx_value()?;</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a><span class="co">/// ```</span></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> XxxConfigExt <span class="op">{</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a> <span class="co">/// Documentation de la méthode getter</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_xxx_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Type<span class="op">&gt;;</span></span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a> <span class="co">/// Documentation de la méthode setter</span></span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_xxx_value(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> value<span class="op">:</span> Type) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> XxxConfigExt <span class="cf">for</span> Config <span class="op">{</span></span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_xxx_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Type<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a> <span class="co">// Implémentation</span></span>
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_xxx_value(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> value<span class="op">:</span> Type) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a> <span class="co">// Implémentation</span></span>
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="patterns-de-chemins-yaml">2. Patterns de chemins YAML</h3>
<p>Les chemins dans la configuration suivent une hiérarchie logique
:</p>
<h4 id="configuration-hôtesystème">Configuration hôte/système</h4>
<div class="sourceCode" id="cb4"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Chemins sous &quot;host&quot;</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;host&quot;</span><span class="op">,</span> <span class="st">&quot;cache_type&quot;</span><span class="op">,</span> <span class="st">&quot;directory&quot;</span>] <span class="co">// Répertoires de cache</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;host&quot;</span><span class="op">,</span> <span class="st">&quot;cache_type&quot;</span><span class="op">,</span> <span class="st">&quot;size&quot;</span>] <span class="co">// Tailles de cache</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;host&quot;</span><span class="op">,</span> <span class="st">&quot;upnp&quot;</span><span class="op">,</span> <span class="st">&quot;manufacturer&quot;</span>] <span class="co">// Configuration UPnP</span></span></pre></div>
<h4 id="configuration-comptesservices">Configuration
comptes/services</h4>
<div class="sourceCode" id="cb5"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Chemins sous &quot;accounts&quot;</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;service&quot;</span><span class="op">,</span> <span class="st">&quot;username&quot;</span>]</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;service&quot;</span><span class="op">,</span> <span class="st">&quot;password&quot;</span>]</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;service&quot;</span><span class="op">,</span> <span class="st">&quot;auth_token&quot;</span>]</span></pre></div>
<h4 id="configuration-sources">Configuration sources</h4>
<div class="sourceCode" id="cb6"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Chemins sous &quot;sources&quot;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;sources&quot;</span><span class="op">,</span> <span class="st">&quot;source_name&quot;</span><span class="op">,</span> <span class="st">&quot;enabled&quot;</span>]</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="op">&amp;</span>[<span class="st">&quot;sources&quot;</span><span class="op">,</span> <span class="st">&quot;source_name&quot;</span><span class="op">,</span> <span class="st">&quot;default_channel&quot;</span>]</span></pre></div>
<h3 id="patterns-de-getters">3. Patterns de getters</h3>
<h4 id="getter-simple-avec-valeur-par-défaut">Getter simple avec valeur
par défaut</h4>
<div class="sourceCode" id="cb7"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Type<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;value&quot;</span>]) <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Type(v)) <span class="op">=&gt;</span> <span class="cn">Ok</span>(v)<span class="op">,</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Ok</span>(DEFAULT_VALUE)<span class="op">,</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="getter-avec-auto-persistence">Getter avec auto-persistence</h4>
<p>Pour les valeurs qui doivent être visibles dans le fichier YAML, le
getter persiste automatiquement la valeur par défaut :</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;enabled&quot;</span>]) <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Bool(b)) <span class="op">=&gt;</span> <span class="cn">Ok</span>(b)<span class="op">,</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="co">// Auto-persist la valeur par défaut</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_xxx_enabled(<span class="cn">true</span>)<span class="op">?;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="cn">true</span>)</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<p><strong>Avantage</strong> : Lutilisateur voit la configuration
effective dans le YAML et peut la modifier facilement.</p>
<h4 id="getter-optionnel">Getter optionnel</h4>
<p>Pour les valeurs vraiment optionnelles (pas de défaut significatif)
:</p>
<div class="sourceCode" id="cb9"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_optional(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;optional&quot;</span>]) <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span><span class="dt">String</span>(s)) <span class="cf">if</span> <span class="op">!</span>s<span class="op">.</span>is_empty() <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">Some</span>(s))<span class="op">,</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span><span class="dt">String</span>(_)) <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">None</span>)<span class="op">,</span> <span class="co">// String vide</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(_) <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">None</span>)<span class="op">,</span> <span class="co">// Mauvais type</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> <span class="cn">Err</span>(_) <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">None</span>)<span class="op">,</span> <span class="co">// Non configuré</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="getter-avec-traitement-spécial">Getter avec traitement
spécial</h4>
<h5 id="déchiffrement-de-mots-de-passe">Déchiffrement de mots de
passe</h5>
<div class="sourceCode" id="cb10"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_password(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;xxx&quot;</span><span class="op">,</span> <span class="st">&quot;password&quot;</span>])<span class="op">?</span> <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(s) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> <span class="co">// Déchiffrement automatique si chiffré</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> <span class="pp">pmoconfig::encryption::</span>get_password(<span class="op">&amp;</span>s)</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map_err(<span class="op">|</span>e<span class="op">|</span> <span class="pp">anyhow!</span>(<span class="st">&quot;Failed to decrypt password: {}&quot;</span><span class="op">,</span> e))</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Err</span>(<span class="pp">anyhow!</span>(<span class="st">&quot;Password not configured&quot;</span>))<span class="op">,</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h5 id="parsing-avec-fallback">Parsing avec fallback</h5>
<div class="sourceCode" id="cb11"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_enum_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>EnumType<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;value&quot;</span>]) <span class="op">{</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span><span class="dt">String</span>(s)) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> s<span class="op">.</span><span class="pp">parse::</span><span class="op">&lt;</span>EnumType<span class="op">&gt;</span>() <span class="op">{</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(kind) <span class="op">=&gt;</span> <span class="cn">Ok</span>(kind)<span class="op">,</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> <span class="cn">Err</span>(_) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="co">// Valeur invalide, utiliser et persister le défaut</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_xxx_enum_value(DEFAULT_ENUM)<span class="op">?;</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(DEFAULT_ENUM)</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Number(n)) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a> <span class="co">// Accepter aussi les valeurs numériques</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">let</span> <span class="cn">Some</span>(id) <span class="op">=</span> n<span class="op">.</span>as_u64() <span class="op">{</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a> <span class="pp">EnumType::</span>from_id(id <span class="kw">as</span> <span class="dt">u8</span>)</span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_xxx_enum_value(DEFAULT_ENUM)<span class="op">?;</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(DEFAULT_ENUM)</span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// Non configuré, persister le défaut</span></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_xxx_enum_value(DEFAULT_ENUM)<span class="op">?;</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(DEFAULT_ENUM)</span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="patterns-de-setters">4. Patterns de setters</h3>
<h4 id="setter-simple">Setter simple</h4>
<div class="sourceCode" id="cb12"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> set_xxx_value(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> value<span class="op">:</span> Type) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;value&quot;</span>]<span class="op">,</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span>Type(value<span class="op">.</span>into())</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="setter-avec-transformation">Setter avec transformation</h4>
<div class="sourceCode" id="cb13"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> set_xxx_enum(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> variant<span class="op">:</span> EnumVariant) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Stocker sous forme conviviale (string) plutôt que numérique</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> name <span class="op">=</span> variant<span class="op">.</span>as_str()<span class="op">;</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;enum&quot;</span>]<span class="op">,</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(name<span class="op">.</span>to_string())</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="setter-multiple-transaction">Setter multiple (transaction)</h4>
<div class="sourceCode" id="cb14"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> set_xxx_auth_info(</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> token<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> user_id<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> expires_at<span class="op">:</span> <span class="dt">u64</span><span class="op">,</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> <span class="co">// Grouper les modifications liées</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;xxx&quot;</span><span class="op">,</span> <span class="st">&quot;auth_token&quot;</span>]<span class="op">,</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(token<span class="op">.</span>to_string())</span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a> )<span class="op">?;</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;xxx&quot;</span><span class="op">,</span> <span class="st">&quot;user_id&quot;</span>]<span class="op">,</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(user_id<span class="op">.</span>to_string())</span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a> )<span class="op">?;</span></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;xxx&quot;</span><span class="op">,</span> <span class="st">&quot;token_expires_at&quot;</span>]<span class="op">,</span></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span>Number(<span class="pp">serde_yaml::Number::</span>from(expires_at))</span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a> )<span class="op">?;</span></span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(())</span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="setter-de-nettoyage">Setter de nettoyage</h4>
<div class="sourceCode" id="cb15"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> clear_xxx_info(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Ne pas propager les erreurs (valeurs peuvent ne pas exister)</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> _ <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>set_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;field1&quot;</span>]<span class="op">,</span> <span class="pp">Value::</span><span class="dt">String</span>(<span class="dt">String</span><span class="pp">::</span>new()))<span class="op">;</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> _ <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>set_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;field2&quot;</span>]<span class="op">,</span> <span class="pp">Value::</span>Number(<span class="pp">Number::</span>from(<span class="dv">0</span>)))<span class="op">;</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(())</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="helpers-de-haut-niveau">5. Helpers de haut niveau</h3>
<h4 id="helper-de-validation">Helper de validation</h4>
<div class="sourceCode" id="cb16"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> is_xxx_valid(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span> <span class="op">{</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Vérifier plusieurs conditions sans Result</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> has_token <span class="op">=</span> <span class="kw">self</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>get_xxx_token()</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>ok()</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>flatten()</span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map(<span class="op">|</span>t<span class="op">|</span> <span class="op">!</span>t<span class="op">.</span>is_empty())</span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>unwrap_or(<span class="cn">false</span>)<span class="op">;</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> has_user <span class="op">=</span> <span class="kw">self</span></span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>get_xxx_user()</span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>ok()</span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>flatten()</span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map(<span class="op">|</span>u<span class="op">|</span> <span class="op">!</span>u<span class="op">.</span>is_empty())</span>
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>unwrap_or(<span class="cn">false</span>)<span class="op">;</span></span>
<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb16-17"><a href="#cb16-17" aria-hidden="true" tabindex="-1"></a> has_token <span class="op">&amp;&amp;</span> has_user</span>
<span id="cb16-18"><a href="#cb16-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="factory-method">Factory method</h4>
<p>Pour les crates qui fournissent des objets complexes configurables
:</p>
<div class="sourceCode" id="cb17"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> create_xxx_cache(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Arc<span class="op">&lt;</span>Cache<span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> dir <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_xxx_dir()<span class="op">?;</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> size <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_xxx_size()<span class="op">?;</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Arc::</span>new(<span class="kw">crate</span><span class="pp">::</span>new_cache(<span class="op">&amp;</span>dir<span class="op">,</span> size)<span class="op">?</span>))</span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> create_xxx_client(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>XxxClient<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> (username<span class="op">,</span> password) <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_xxx_credentials()<span class="op">?;</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a> <span class="pp">XxxClient::</span>builder()</span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>credentials(username<span class="op">,</span> password)</span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>cache_dir(<span class="kw">self</span><span class="op">.</span>get_xxx_cache_dir()<span class="op">?</span>)</span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>build()</span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="getter-combiné">Getter combiné</h4>
<div class="sourceCode" id="cb18"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_credentials(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>(<span class="dt">String</span><span class="op">,</span> <span class="dt">String</span>)<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> username <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_xxx_username()<span class="op">?;</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> password <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_xxx_password()<span class="op">?;</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>((username<span class="op">,</span> password))</span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="utilisation-des-méthodes-pmoconfig">6. Utilisation des méthodes
pmoconfig</h3>
<h4 id="répertoires-managés">Répertoires managés</h4>
<p>Pour les répertoires qui doivent être créés automatiquement :</p>
<div class="sourceCode" id="cb19"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_cache_dir(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// get_managed_dir crée le répertoire s&#39;il n&#39;existe pas</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>get_managed_dir(<span class="op">&amp;</span>[<span class="st">&quot;host&quot;</span><span class="op">,</span> <span class="st">&quot;xxx_cache&quot;</span><span class="op">,</span> <span class="st">&quot;directory&quot;</span>]<span class="op">,</span> <span class="st">&quot;cache_xxx&quot;</span>)</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> set_xxx_cache_dir(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> directory<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_managed_dir(<span class="op">&amp;</span>[<span class="st">&quot;host&quot;</span><span class="op">,</span> <span class="st">&quot;xxx_cache&quot;</span><span class="op">,</span> <span class="st">&quot;directory&quot;</span>]<span class="op">,</span> directory)</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="méthodes-génériques-de-config-utilisables">Méthodes génériques
de Config utilisables</h4>
<div class="sourceCode" id="cb20"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Lecture de valeur générique</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">fn</span> get_value(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> path<span class="op">:</span> <span class="op">&amp;</span>[<span class="op">&amp;</span><span class="dt">str</span>]) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Value<span class="op">&gt;</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Écriture de valeur générique</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">fn</span> set_value(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> path<span class="op">:</span> <span class="op">&amp;</span>[<span class="op">&amp;</span><span class="dt">str</span>]<span class="op">,</span> value<span class="op">:</span> Value) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Répertoires managés (création auto)</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">fn</span> get_managed_dir(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> path<span class="op">:</span> <span class="op">&amp;</span>[<span class="op">&amp;</span><span class="dt">str</span>]<span class="op">,</span> <span class="kw">default</span><span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">fn</span> set_managed_dir(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> path<span class="op">:</span> <span class="op">&amp;</span>[<span class="op">&amp;</span><span class="dt">str</span>]<span class="op">,</span> directory<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Déchiffrement de mots de passe</span></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">mod</span> encryption <span class="op">{</span></span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">pub</span> <span class="kw">fn</span> encrypt_password(password<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">pub</span> <span class="kw">fn</span> decrypt_password(encrypted<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb20-15"><a href="#cb20-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">pub</span> <span class="kw">fn</span> get_password(value<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="co">// Auto-détection</span></span>
<span id="cb20-16"><a href="#cb20-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">pub</span> <span class="kw">fn</span> is_encrypted(value<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">bool</span></span>
<span id="cb20-17"><a href="#cb20-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h2 id="patterns-spécialisés">Patterns spécialisés</h2>
<h3 id="pattern-cache-pmocache">Pattern cache (pmocache)</h3>
<p>Le crate <code>pmocache</code> fournit un trait générique
<code>CacheConfigExt</code> que les autres crates de cache peuvent
utiliser :</p>
<div class="sourceCode" id="cb21"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmocache::</span>CacheConfigExt<span class="op">;</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> AudioCacheConfigExt <span class="cf">for</span> Config <span class="op">{</span></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_audiocache_dir(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>get_cache_dir(<span class="st">&quot;audio_cache&quot;</span><span class="op">,</span> DEFAULT_AUDIO_CACHE_DIR)</span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> create_audio_cache(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Arc<span class="op">&lt;</span>Cache<span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> dir <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_audiocache_dir()<span class="op">?;</span></span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> size <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_audiocache_size()<span class="op">?;</span></span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Arc::</span>new(<span class="kw">crate</span><span class="pp">::cache::</span>new_cache(<span class="op">&amp;</span>dir<span class="op">,</span> size)<span class="op">?</span>))</span>
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<p><strong>Avantage</strong> : Cohérence entre tous les caches (audio,
covers, qobuz, etc.)</p>
<h3 id="pattern-authentification-pmoqobuz">Pattern authentification
(pmoqobuz)</h3>
<p>Pour les services nécessitant une authentification :</p>
<div class="sourceCode" id="cb22"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> QobuzConfigExt <span class="op">{</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Credentials de base</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_password(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span> <span class="co">// Auto-decrypt</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_credentials(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>(<span class="dt">String</span><span class="op">,</span> <span class="dt">String</span>)<span class="op">&gt;;</span></span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a> <span class="co">// Tokens d&#39;authentification</span></span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_auth_token(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;;</span></span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_user_id(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;;</span></span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_token_expires_at(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">u64</span><span class="op">&gt;&gt;;</span></span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// Gestion d&#39;authentification groupée</span></span>
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_auth_info(</span>
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> </span>
<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a> token<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span> </span>
<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a> user_id<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span> </span>
<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a> expires_at<span class="op">:</span> <span class="dt">u64</span></span>
<span id="cb22-18"><a href="#cb22-18" aria-hidden="true" tabindex="-1"></a> ) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb22-19"><a href="#cb22-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> clear_qobuz_auth_info(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb22-20"><a href="#cb22-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-21"><a href="#cb22-21" aria-hidden="true" tabindex="-1"></a> <span class="co">// Validation</span></span>
<span id="cb22-22"><a href="#cb22-22" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> is_qobuz_auth_valid(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span><span class="op">;</span></span>
<span id="cb22-23"><a href="#cb22-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="pattern-rate-limiting-pmoqobuz">Pattern rate limiting
(pmoqobuz)</h3>
<p>Pour les services avec rate limiting :</p>
<div class="sourceCode" id="cb23"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> QobuzConfigExt <span class="op">{</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_rate_limit_max_concurrent(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">usize</span><span class="op">&gt;&gt;;</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_rate_limit_max_concurrent(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> max<span class="op">:</span> <span class="dt">usize</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_rate_limit_min_delay_ms(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">u64</span><span class="op">&gt;&gt;;</span></span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_rate_limit_min_delay_ms(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> delay_ms<span class="op">:</span> <span class="dt">u64</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> is_qobuz_rate_limiting_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span><span class="op">;</span></span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_rate_limiting_enabled(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> enabled<span class="op">:</span> <span class="dt">bool</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb23-10"><a href="#cb23-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="pattern-configuration-minimale-pmoparadise">Pattern
configuration minimale (pmoparadise)</h3>
<p>Pour les sources qui nécessitent peu de configuration :</p>
<div class="sourceCode" id="cb24"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> RadioParadiseConfigExt <span class="op">{</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// Juste enable/disable</span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_paradise_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;;</span></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_paradise_enabled(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> enabled<span class="op">:</span> <span class="dt">bool</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a> <span class="co">// Configuration minimale avec valeurs intelligentes par défaut</span></span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_paradise_default_channel(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">u8</span><span class="op">&gt;;</span></span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_paradise_default_channel(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> channel<span class="op">:</span> <span class="dt">u8</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<p><strong>Philosophie</strong> : Ne configurer que ce qui doit vraiment
lêtre. Éviter la sur-configuration.</p>
<h3 id="pattern-upnp-pmoupnp">Pattern UPnP (pmoupnp)</h3>
<p>Pour la configuration des devices UPnP :</p>
<div class="sourceCode" id="cb25"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> UpnpConfigExt <span class="op">{</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_upnp_manufacturer(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_upnp_manufacturer(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> manufacturer<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_upnp_udn_prefix(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_upnp_udn_prefix(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> prefix<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_upnp_model_name_prefix(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_upnp_model_name_prefix(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> prefix<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_upnp_friendly_name_prefix(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_upnp_friendly_name_prefix(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> prefix<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<p><strong>Usage</strong> : Différencier plusieurs instances du serveur
(dev, prod, test).</p>
<h2 id="bonnes-pratiques">Bonnes pratiques</h2>
<h3 id="nommage-des-méthodes">1. Nommage des méthodes</h3>
<div class="sourceCode" id="cb26"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Préfixer avec le nom du service/composant</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_cache_dir(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> cache_type<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span> <span class="kw">default</span><span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_paradise_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;</span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a><span class="co">// ❌ MAUVAIS : Nom trop générique</span></span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_directory(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> is_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span></span></pre></div>
<h3 id="gestion-des-erreurs">2. Gestion des erreurs</h3>
<div class="sourceCode" id="cb27"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Retourner Result pour les valeurs obligatoires</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;xxx&quot;</span><span class="op">,</span> <span class="st">&quot;username&quot;</span>])<span class="op">?</span> <span class="op">{</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(s) <span class="op">=&gt;</span> <span class="cn">Ok</span>(s)<span class="op">,</span></span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Err</span>(<span class="pp">anyhow!</span>(<span class="st">&quot;XXX username not configured&quot;</span>))<span class="op">,</span></span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Retourner Option pour les valeurs optionnelles</span></span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_token(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;</span></span>
<span id="cb27-11"><a href="#cb27-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-12"><a href="#cb27-12" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Retourner bool pour les checks (sans erreur)</span></span>
<span id="cb27-13"><a href="#cb27-13" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> is_xxx_valid(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span></span>
<span id="cb27-14"><a href="#cb27-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-15"><a href="#cb27-15" aria-hidden="true" tabindex="-1"></a><span class="co">// ❌ MAUVAIS : Panic ou unwrap</span></span>
<span id="cb27-16"><a href="#cb27-16" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">String</span> <span class="op">{</span></span>
<span id="cb27-17"><a href="#cb27-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span>])<span class="op">.</span>unwrap()<span class="op">.</span>as_str()<span class="op">.</span>unwrap()</span>
<span id="cb27-18"><a href="#cb27-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="valeurs-par-défaut">3. Valeurs par défaut</h3>
<div class="sourceCode" id="cb28"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Constantes en haut du fichier</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> DEFAULT_CACHE_SIZE<span class="op">:</span> <span class="dt">usize</span> <span class="op">=</span> <span class="dv">500</span><span class="op">;</span></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> DEFAULT_CACHE_DIR<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span> <span class="op">=</span> <span class="st">&quot;cache_audio&quot;</span><span class="op">;</span></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Valeurs par défaut documentées</span></span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a><span class="co">/// Récupère la taille du cache</span></span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a><span class="co">/// # Returns</span></span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a><span class="co">/// Le nombre maximal d&#39;éléments (default: 500)</span></span>
<span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_cache_size(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">usize</span><span class="op">&gt;</span></span>
<span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a><span class="co">// ❌ MAUVAIS : Magic numbers</span></span>
<span id="cb28-14"><a href="#cb28-14" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_cache_size(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">usize</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb28-15"><a href="#cb28-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;cache&quot;</span><span class="op">,</span> <span class="st">&quot;size&quot;</span>]) <span class="op">{</span></span>
<span id="cb28-16"><a href="#cb28-16" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Number(n)) <span class="op">=&gt;</span> <span class="cn">Ok</span>(n<span class="op">.</span>as_u64()<span class="op">.</span>unwrap() <span class="kw">as</span> <span class="dt">usize</span>)<span class="op">,</span></span>
<span id="cb28-17"><a href="#cb28-17" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="dv">500</span>)<span class="op">,</span> <span class="co">// Où vient ce 500 ?</span></span>
<span id="cb28-18"><a href="#cb28-18" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb28-19"><a href="#cb28-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="documentation">4. Documentation</h3>
<p>Chaque méthode doit avoir :</p>
<div class="sourceCode" id="cb29"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="co">/// Description courte de ce que fait la méthode</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a><span class="co">/// # Arguments (si applicable)</span></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a><span class="co">/// * `param` - Description du paramètre</span></span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a><span class="co">/// # Returns</span></span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a><span class="co">/// Description de ce qui est retourné (avec valeur par défaut si applicable)</span></span>
<span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a><span class="co">/// # Errors (si Result)</span></span>
<span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a><span class="co">/// Description des cas d&#39;erreur</span></span>
<span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a><span class="co">/// # Exemple</span></span>
<span id="cb29-16"><a href="#cb29-16" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-17"><a href="#cb29-17" aria-hidden="true" tabindex="-1"></a><span class="co">/// ```rust,ignore</span></span>
<span id="cb29-18"><a href="#cb29-18" aria-hidden="true" tabindex="-1"></a><span class="co">/// use pmoconfig::get_config;</span></span>
<span id="cb29-19"><a href="#cb29-19" aria-hidden="true" tabindex="-1"></a><span class="co">/// use pmoxxx::XxxConfigExt;</span></span>
<span id="cb29-20"><a href="#cb29-20" aria-hidden="true" tabindex="-1"></a><span class="co">///</span></span>
<span id="cb29-21"><a href="#cb29-21" aria-hidden="true" tabindex="-1"></a><span class="co">/// let config = get_config();</span></span>
<span id="cb29-22"><a href="#cb29-22" aria-hidden="true" tabindex="-1"></a><span class="co">/// let value = config.get_xxx_value()?;</span></span>
<span id="cb29-23"><a href="#cb29-23" aria-hidden="true" tabindex="-1"></a><span class="co">/// ```</span></span>
<span id="cb29-24"><a href="#cb29-24" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Type<span class="op">&gt;;</span></span></pre></div>
<h3 id="organisation-du-code">5. Organisation du code</h3>
<h4 id="structure-du-fichier-config_ext.rs-1">Structure du fichier
config_ext.rs</h4>
<div class="sourceCode" id="cb30"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="co">//! Documentation du module</span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Imports</span></span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">anyhow::</span><span class="dt">Result</span><span class="op">;</span></span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmoconfig::</span>Config<span class="op">;</span></span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">serde_yaml::</span>Value<span class="op">;</span></span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Constantes</span></span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> DEFAULT_XXX<span class="op">:</span> Type <span class="op">=</span> value<span class="op">;</span></span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Trait</span></span>
<span id="cb30-12"><a href="#cb30-12" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> XxxConfigExt <span class="op">{</span></span>
<span id="cb30-13"><a href="#cb30-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// Méthodes groupées logiquement</span></span>
<span id="cb30-14"><a href="#cb30-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb30-15"><a href="#cb30-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-16"><a href="#cb30-16" aria-hidden="true" tabindex="-1"></a><span class="co">// Implémentation</span></span>
<span id="cb30-17"><a href="#cb30-17" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> XxxConfigExt <span class="cf">for</span> Config <span class="op">{</span></span>
<span id="cb30-18"><a href="#cb30-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// Méthodes dans le même ordre que le trait</span></span>
<span id="cb30-19"><a href="#cb30-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb30-20"><a href="#cb30-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-21"><a href="#cb30-21" aria-hidden="true" tabindex="-1"></a><span class="co">// Tests (optionnel)</span></span>
<span id="cb30-22"><a href="#cb30-22" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>cfg<span class="at">(</span>test<span class="at">)]</span></span>
<span id="cb30-23"><a href="#cb30-23" aria-hidden="true" tabindex="-1"></a><span class="kw">mod</span> tests <span class="op">{</span></span>
<span id="cb30-24"><a href="#cb30-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">use</span> <span class="kw">super</span><span class="pp">::</span><span class="op">*;</span></span>
<span id="cb30-25"><a href="#cb30-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h4 id="ordre-des-méthodes-dans-le-trait">Ordre des méthodes dans le
trait</h4>
<ol type="1">
<li>Getters/setters simples</li>
<li>Getters/setters combinés</li>
<li>Helpers de validation</li>
<li>Factory methods</li>
<li>Méthodes de nettoyage</li>
</ol>
<div class="sourceCode" id="cb31"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> QobuzConfigExt <span class="op">{</span></span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// 1. Getters/setters simples</span></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> username<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_password(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_password(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> password<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a> <span class="co">// 2. Getters/setters combinés</span></span>
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_credentials(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>(<span class="dt">String</span><span class="op">,</span> <span class="dt">String</span>)<span class="op">&gt;;</span></span>
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_auth_info(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> <span class="op">...</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// 3. Helpers de validation</span></span>
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> is_qobuz_auth_valid(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span><span class="op">;</span></span>
<span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb31-15"><a href="#cb31-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// 4. Factory methods</span></span>
<span id="cb31-16"><a href="#cb31-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> create_qobuz_client(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>QobuzClient<span class="op">&gt;;</span></span>
<span id="cb31-17"><a href="#cb31-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb31-18"><a href="#cb31-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// 5. Méthodes de nettoyage</span></span>
<span id="cb31-19"><a href="#cb31-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> clear_qobuz_auth_info(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb31-20"><a href="#cb31-20" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="types-de-retour">6. Types de retour</h3>
<div class="sourceCode" id="cb32"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Result&lt;T&gt; pour les opérations qui peuvent échouer</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Result&lt;Option&lt;T&gt;&gt; pour les valeurs optionnelles</span></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_token(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;</span></span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : bool pour les checks simples</span></span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> is_xxx_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span></span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Result&lt;(T1, T2)&gt; pour retourner plusieurs valeurs liées</span></span>
<span id="cb32-11"><a href="#cb32-11" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_credentials(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>(<span class="dt">String</span><span class="op">,</span> <span class="dt">String</span>)<span class="op">&gt;</span></span>
<span id="cb32-12"><a href="#cb32-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-13"><a href="#cb32-13" aria-hidden="true" tabindex="-1"></a><span class="co">// ❌ MAUVAIS : Option&lt;Result&lt;T&gt;&gt; (ordre inversé)</span></span>
<span id="cb32-14"><a href="#cb32-14" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Option</span><span class="op">&lt;</span><span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;</span></span></pre></div>
<h3 id="conversion-de-types">7. Conversion de types</h3>
<div class="sourceCode" id="cb33"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Gérer plusieurs types d&#39;entrée</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_value(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">u64</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span><span class="op">,</span> <span class="st">&quot;to&quot;</span><span class="op">,</span> <span class="st">&quot;value&quot;</span>]) <span class="op">{</span></span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Number(n)) <span class="cf">if</span> n<span class="op">.</span>is_u64() <span class="op">=&gt;</span> <span class="cn">Ok</span>(n<span class="op">.</span>as_u64()<span class="op">.</span>unwrap())<span class="op">,</span></span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Number(n)) <span class="cf">if</span> n<span class="op">.</span>is_i64() <span class="op">=&gt;</span> <span class="cn">Ok</span>(n<span class="op">.</span>as_i64()<span class="op">.</span>unwrap() <span class="kw">as</span> <span class="dt">u64</span>)<span class="op">,</span></span>
<span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span><span class="dt">String</span>(s)) <span class="op">=&gt;</span> s<span class="op">.</span><span class="pp">parse::</span><span class="op">&lt;</span><span class="dt">u64</span><span class="op">&gt;</span>()</span>
<span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map_err(<span class="op">|</span>e<span class="op">|</span> <span class="pp">anyhow!</span>(<span class="st">&quot;Invalid number: {}&quot;</span><span class="op">,</span> e))<span class="op">,</span></span>
<span id="cb33-8"><a href="#cb33-8" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Err</span>(<span class="pp">anyhow!</span>(<span class="st">&quot;Value not configured&quot;</span>))<span class="op">,</span></span>
<span id="cb33-9"><a href="#cb33-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb33-10"><a href="#cb33-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb33-11"><a href="#cb33-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb33-12"><a href="#cb33-12" aria-hidden="true" tabindex="-1"></a><span class="co">// ✅ BON : Convertir en format convivial pour l&#39;utilisateur</span></span>
<span id="cb33-13"><a href="#cb33-13" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> set_xxx_channel(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> channel<span class="op">:</span> <span class="dt">u8</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb33-14"><a href="#cb33-14" aria-hidden="true" tabindex="-1"></a> <span class="co">// Stocker &quot;main&quot; au lieu de &quot;0&quot; dans le YAML</span></span>
<span id="cb33-15"><a href="#cb33-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> name <span class="op">=</span> <span class="cf">match</span> channel <span class="op">{</span></span>
<span id="cb33-16"><a href="#cb33-16" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span> <span class="op">=&gt;</span> <span class="st">&quot;main&quot;</span><span class="op">,</span></span>
<span id="cb33-17"><a href="#cb33-17" aria-hidden="true" tabindex="-1"></a> <span class="dv">1</span> <span class="op">=&gt;</span> <span class="st">&quot;mellow&quot;</span><span class="op">,</span></span>
<span id="cb33-18"><a href="#cb33-18" aria-hidden="true" tabindex="-1"></a> <span class="dv">2</span> <span class="op">=&gt;</span> <span class="st">&quot;rock&quot;</span><span class="op">,</span></span>
<span id="cb33-19"><a href="#cb33-19" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cf">return</span> <span class="cn">Err</span>(<span class="pp">anyhow!</span>(<span class="st">&quot;Invalid channel&quot;</span>))<span class="op">,</span></span>
<span id="cb33-20"><a href="#cb33-20" aria-hidden="true" tabindex="-1"></a> <span class="op">};</span></span>
<span id="cb33-21"><a href="#cb33-21" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(<span class="op">&amp;</span>[<span class="st">&quot;path&quot;</span>]<span class="op">,</span> <span class="pp">Value::</span><span class="dt">String</span>(name<span class="op">.</span>to_string()))</span>
<span id="cb33-22"><a href="#cb33-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h2 id="exemples-dimplémentation-complète">Exemples dimplémentation
complète</h2>
<h3 id="exemple-1-cache-simple-pmocovers">Exemple 1 : Cache simple
(pmocovers)</h3>
<div class="sourceCode" id="cb34"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="co">//! Extension pour intégrer le cache de couvertures dans pmoconfig</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">anyhow::</span><span class="dt">Result</span><span class="op">;</span></span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmocache::</span>CacheConfigExt<span class="op">;</span></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmoconfig::</span>Config<span class="op">;</span></span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">std::sync::</span>Arc<span class="op">;</span></span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> DEFAULT_COVER_CACHE_DIR<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span> <span class="op">=</span> <span class="st">&quot;cache_covers&quot;</span><span class="op">;</span></span>
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> DEFAULT_COVER_CACHE_SIZE<span class="op">:</span> <span class="dt">usize</span> <span class="op">=</span> <span class="dv">2000</span><span class="op">;</span></span>
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-11"><a href="#cb34-11" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> CoverCacheConfigExt <span class="op">{</span></span>
<span id="cb34-12"><a href="#cb34-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_covers_dir(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb34-13"><a href="#cb34-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_covers_dir(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> directory<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb34-14"><a href="#cb34-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_covers_size(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">usize</span><span class="op">&gt;;</span></span>
<span id="cb34-15"><a href="#cb34-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_covers_size(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> size<span class="op">:</span> <span class="dt">usize</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb34-16"><a href="#cb34-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> create_cover_cache(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Arc<span class="op">&lt;</span><span class="kw">crate</span><span class="pp">::</span>Cache<span class="op">&gt;&gt;;</span></span>
<span id="cb34-17"><a href="#cb34-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb34-18"><a href="#cb34-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-19"><a href="#cb34-19" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> CoverCacheConfigExt <span class="cf">for</span> Config <span class="op">{</span></span>
<span id="cb34-20"><a href="#cb34-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_covers_dir(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb34-21"><a href="#cb34-21" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>get_cache_dir(<span class="st">&quot;cover_cache&quot;</span><span class="op">,</span> DEFAULT_COVER_CACHE_DIR)</span>
<span id="cb34-22"><a href="#cb34-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb34-23"><a href="#cb34-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-24"><a href="#cb34-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_covers_dir(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> directory<span class="op">:</span> <span class="dt">String</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb34-25"><a href="#cb34-25" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_cache_dir(<span class="st">&quot;cover_cache&quot;</span><span class="op">,</span> directory)</span>
<span id="cb34-26"><a href="#cb34-26" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb34-27"><a href="#cb34-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-28"><a href="#cb34-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_covers_size(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">usize</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb34-29"><a href="#cb34-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>get_cache_size(<span class="st">&quot;cover_cache&quot;</span><span class="op">,</span> DEFAULT_COVER_CACHE_SIZE)</span>
<span id="cb34-30"><a href="#cb34-30" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb34-31"><a href="#cb34-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-32"><a href="#cb34-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_covers_size(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> size<span class="op">:</span> <span class="dt">usize</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb34-33"><a href="#cb34-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_cache_size(<span class="st">&quot;cover_cache&quot;</span><span class="op">,</span> size)</span>
<span id="cb34-34"><a href="#cb34-34" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb34-35"><a href="#cb34-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-36"><a href="#cb34-36" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> create_cover_cache(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Arc<span class="op">&lt;</span><span class="kw">crate</span><span class="pp">::</span>Cache<span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb34-37"><a href="#cb34-37" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> dir <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_covers_dir()<span class="op">?;</span></span>
<span id="cb34-38"><a href="#cb34-38" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> size <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_covers_size()<span class="op">?;</span></span>
<span id="cb34-39"><a href="#cb34-39" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Arc::</span>new(<span class="kw">crate</span><span class="pp">::cache::</span>new_cache(<span class="op">&amp;</span>dir<span class="op">,</span> size)<span class="op">?</span>))</span>
<span id="cb34-40"><a href="#cb34-40" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb34-41"><a href="#cb34-41" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3
id="exemple-2-service-avec-authentification-pmoqobuz---simplifié">Exemple
2 : Service avec authentification (pmoqobuz - simplifié)</h3>
<div class="sourceCode" id="cb35"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="co">//! Extension pour intégrer la configuration Qobuz dans pmoconfig</span></span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">anyhow::</span><span class="op">{</span>anyhow<span class="op">,</span> <span class="dt">Result</span><span class="op">};</span></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmoconfig::</span>Config<span class="op">;</span></span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">serde_yaml::</span>Value<span class="op">;</span></span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> QobuzConfigExt <span class="op">{</span></span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a> <span class="co">// Credentials</span></span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> username<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_password(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;;</span></span>
<span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_password(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> password<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_credentials(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>(<span class="dt">String</span><span class="op">,</span> <span class="dt">String</span>)<span class="op">&gt;;</span></span>
<span id="cb35-14"><a href="#cb35-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-15"><a href="#cb35-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// Authentification</span></span>
<span id="cb35-16"><a href="#cb35-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_auth_token(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;;</span></span>
<span id="cb35-17"><a href="#cb35-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_user_id(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;;</span></span>
<span id="cb35-18"><a href="#cb35-18" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_auth_info(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> token<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span> user_id<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb35-19"><a href="#cb35-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> clear_qobuz_auth_info(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb35-20"><a href="#cb35-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> is_qobuz_auth_valid(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span><span class="op">;</span></span>
<span id="cb35-21"><a href="#cb35-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb35-22"><a href="#cb35-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-23"><a href="#cb35-23" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> QobuzConfigExt <span class="cf">for</span> Config <span class="op">{</span></span>
<span id="cb35-24"><a href="#cb35-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb35-25"><a href="#cb35-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;username&quot;</span>])<span class="op">?</span> <span class="op">{</span></span>
<span id="cb35-26"><a href="#cb35-26" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(s) <span class="op">=&gt;</span> <span class="cn">Ok</span>(s)<span class="op">,</span></span>
<span id="cb35-27"><a href="#cb35-27" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Err</span>(<span class="pp">anyhow!</span>(<span class="st">&quot;Qobuz username not configured&quot;</span>))<span class="op">,</span></span>
<span id="cb35-28"><a href="#cb35-28" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-29"><a href="#cb35-29" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-30"><a href="#cb35-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-31"><a href="#cb35-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_username(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> username<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb35-32"><a href="#cb35-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb35-33"><a href="#cb35-33" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;username&quot;</span>]<span class="op">,</span></span>
<span id="cb35-34"><a href="#cb35-34" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(username<span class="op">.</span>to_string())<span class="op">,</span></span>
<span id="cb35-35"><a href="#cb35-35" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb35-36"><a href="#cb35-36" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-37"><a href="#cb35-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-38"><a href="#cb35-38" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_password(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb35-39"><a href="#cb35-39" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;password&quot;</span>])<span class="op">?</span> <span class="op">{</span></span>
<span id="cb35-40"><a href="#cb35-40" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(s) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb35-41"><a href="#cb35-41" aria-hidden="true" tabindex="-1"></a> <span class="co">// Déchiffrement automatique</span></span>
<span id="cb35-42"><a href="#cb35-42" aria-hidden="true" tabindex="-1"></a> <span class="pp">pmoconfig::encryption::</span>get_password(<span class="op">&amp;</span>s)</span>
<span id="cb35-43"><a href="#cb35-43" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map_err(<span class="op">|</span>e<span class="op">|</span> <span class="pp">anyhow!</span>(<span class="st">&quot;Failed to decrypt password: {}&quot;</span><span class="op">,</span> e))</span>
<span id="cb35-44"><a href="#cb35-44" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-45"><a href="#cb35-45" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Err</span>(<span class="pp">anyhow!</span>(<span class="st">&quot;Qobuz password not configured&quot;</span>))<span class="op">,</span></span>
<span id="cb35-46"><a href="#cb35-46" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-47"><a href="#cb35-47" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-48"><a href="#cb35-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-49"><a href="#cb35-49" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_password(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> password<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb35-50"><a href="#cb35-50" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb35-51"><a href="#cb35-51" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;password&quot;</span>]<span class="op">,</span></span>
<span id="cb35-52"><a href="#cb35-52" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(password<span class="op">.</span>to_string())<span class="op">,</span></span>
<span id="cb35-53"><a href="#cb35-53" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb35-54"><a href="#cb35-54" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-55"><a href="#cb35-55" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-56"><a href="#cb35-56" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_credentials(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>(<span class="dt">String</span><span class="op">,</span> <span class="dt">String</span>)<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb35-57"><a href="#cb35-57" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> username <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_qobuz_username()<span class="op">?;</span></span>
<span id="cb35-58"><a href="#cb35-58" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> password <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>get_qobuz_password()<span class="op">?;</span></span>
<span id="cb35-59"><a href="#cb35-59" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>((username<span class="op">,</span> password))</span>
<span id="cb35-60"><a href="#cb35-60" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-61"><a href="#cb35-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-62"><a href="#cb35-62" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_auth_token(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb35-63"><a href="#cb35-63" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;auth_token&quot;</span>]) <span class="op">{</span></span>
<span id="cb35-64"><a href="#cb35-64" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span><span class="dt">String</span>(s)) <span class="cf">if</span> <span class="op">!</span>s<span class="op">.</span>is_empty() <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">Some</span>(s))<span class="op">,</span></span>
<span id="cb35-65"><a href="#cb35-65" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">None</span>)<span class="op">,</span></span>
<span id="cb35-66"><a href="#cb35-66" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-67"><a href="#cb35-67" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-68"><a href="#cb35-68" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-69"><a href="#cb35-69" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_qobuz_user_id(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Option</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb35-70"><a href="#cb35-70" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;user_id&quot;</span>]) <span class="op">{</span></span>
<span id="cb35-71"><a href="#cb35-71" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span><span class="dt">String</span>(s)) <span class="cf">if</span> <span class="op">!</span>s<span class="op">.</span>is_empty() <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">Some</span>(s))<span class="op">,</span></span>
<span id="cb35-72"><a href="#cb35-72" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="cn">None</span>)<span class="op">,</span></span>
<span id="cb35-73"><a href="#cb35-73" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-74"><a href="#cb35-74" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-75"><a href="#cb35-75" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-76"><a href="#cb35-76" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_qobuz_auth_info(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> token<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span> user_id<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb35-77"><a href="#cb35-77" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb35-78"><a href="#cb35-78" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;auth_token&quot;</span>]<span class="op">,</span></span>
<span id="cb35-79"><a href="#cb35-79" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(token<span class="op">.</span>to_string())<span class="op">,</span></span>
<span id="cb35-80"><a href="#cb35-80" aria-hidden="true" tabindex="-1"></a> )<span class="op">?;</span></span>
<span id="cb35-81"><a href="#cb35-81" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb35-82"><a href="#cb35-82" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;user_id&quot;</span>]<span class="op">,</span></span>
<span id="cb35-83"><a href="#cb35-83" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(user_id<span class="op">.</span>to_string())<span class="op">,</span></span>
<span id="cb35-84"><a href="#cb35-84" aria-hidden="true" tabindex="-1"></a> )<span class="op">?;</span></span>
<span id="cb35-85"><a href="#cb35-85" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(())</span>
<span id="cb35-86"><a href="#cb35-86" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-87"><a href="#cb35-87" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-88"><a href="#cb35-88" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> clear_qobuz_auth_info(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb35-89"><a href="#cb35-89" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> _ <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb35-90"><a href="#cb35-90" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;auth_token&quot;</span>]<span class="op">,</span></span>
<span id="cb35-91"><a href="#cb35-91" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(<span class="dt">String</span><span class="pp">::</span>new())<span class="op">,</span></span>
<span id="cb35-92"><a href="#cb35-92" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb35-93"><a href="#cb35-93" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> _ <span class="op">=</span> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb35-94"><a href="#cb35-94" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;qobuz&quot;</span><span class="op">,</span> <span class="st">&quot;user_id&quot;</span>]<span class="op">,</span></span>
<span id="cb35-95"><a href="#cb35-95" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(<span class="dt">String</span><span class="pp">::</span>new())<span class="op">,</span></span>
<span id="cb35-96"><a href="#cb35-96" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb35-97"><a href="#cb35-97" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(())</span>
<span id="cb35-98"><a href="#cb35-98" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-99"><a href="#cb35-99" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-100"><a href="#cb35-100" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> is_qobuz_auth_valid(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">bool</span> <span class="op">{</span></span>
<span id="cb35-101"><a href="#cb35-101" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>get_qobuz_auth_token()</span>
<span id="cb35-102"><a href="#cb35-102" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>ok()</span>
<span id="cb35-103"><a href="#cb35-103" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>flatten()</span>
<span id="cb35-104"><a href="#cb35-104" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map(<span class="op">|</span>t<span class="op">|</span> <span class="op">!</span>t<span class="op">.</span>is_empty())</span>
<span id="cb35-105"><a href="#cb35-105" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>unwrap_or(<span class="cn">false</span>)</span>
<span id="cb35-106"><a href="#cb35-106" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;&amp;</span> <span class="kw">self</span></span>
<span id="cb35-107"><a href="#cb35-107" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>get_qobuz_user_id()</span>
<span id="cb35-108"><a href="#cb35-108" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>ok()</span>
<span id="cb35-109"><a href="#cb35-109" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>flatten()</span>
<span id="cb35-110"><a href="#cb35-110" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map(<span class="op">|</span>u<span class="op">|</span> <span class="op">!</span>u<span class="op">.</span>is_empty())</span>
<span id="cb35-111"><a href="#cb35-111" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>unwrap_or(<span class="cn">false</span>)</span>
<span id="cb35-112"><a href="#cb35-112" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb35-113"><a href="#cb35-113" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h3 id="exemple-3-configuration-minimale-pmoparadise">Exemple 3 :
Configuration minimale (pmoparadise)</h3>
<div class="sourceCode" id="cb36"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="co">//! Extension pour intégrer Radio Paradise dans pmoconfig</span></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">anyhow::</span><span class="dt">Result</span><span class="op">;</span></span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmoconfig::</span>Config<span class="op">;</span></span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">serde_yaml::</span>Value<span class="op">;</span></span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">trait</span> RadioParadiseConfigExt <span class="op">{</span></span>
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_paradise_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;;</span></span>
<span id="cb36-9"><a href="#cb36-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_paradise_enabled(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> enabled<span class="op">:</span> <span class="dt">bool</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb36-10"><a href="#cb36-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_paradise_default_channel(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">u8</span><span class="op">&gt;;</span></span>
<span id="cb36-11"><a href="#cb36-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_paradise_default_channel(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> channel<span class="op">:</span> <span class="dt">u8</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;;</span></span>
<span id="cb36-12"><a href="#cb36-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb36-13"><a href="#cb36-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-14"><a href="#cb36-14" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> RadioParadiseConfigExt <span class="cf">for</span> Config <span class="op">{</span></span>
<span id="cb36-15"><a href="#cb36-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_paradise_enabled(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb36-16"><a href="#cb36-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;sources&quot;</span><span class="op">,</span> <span class="st">&quot;radio_paradise&quot;</span><span class="op">,</span> <span class="st">&quot;enabled&quot;</span>]) <span class="op">{</span></span>
<span id="cb36-17"><a href="#cb36-17" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Bool(b)) <span class="op">=&gt;</span> <span class="cn">Ok</span>(b)<span class="op">,</span></span>
<span id="cb36-18"><a href="#cb36-18" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb36-19"><a href="#cb36-19" aria-hidden="true" tabindex="-1"></a> <span class="co">// Auto-persist le défaut</span></span>
<span id="cb36-20"><a href="#cb36-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_paradise_enabled(<span class="cn">true</span>)<span class="op">?;</span></span>
<span id="cb36-21"><a href="#cb36-21" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="cn">true</span>)</span>
<span id="cb36-22"><a href="#cb36-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-23"><a href="#cb36-23" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-24"><a href="#cb36-24" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-25"><a href="#cb36-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-26"><a href="#cb36-26" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_paradise_enabled(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> enabled<span class="op">:</span> <span class="dt">bool</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb36-27"><a href="#cb36-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb36-28"><a href="#cb36-28" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;sources&quot;</span><span class="op">,</span> <span class="st">&quot;radio_paradise&quot;</span><span class="op">,</span> <span class="st">&quot;enabled&quot;</span>]<span class="op">,</span></span>
<span id="cb36-29"><a href="#cb36-29" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span>Bool(enabled)<span class="op">,</span></span>
<span id="cb36-30"><a href="#cb36-30" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb36-31"><a href="#cb36-31" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-32"><a href="#cb36-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-33"><a href="#cb36-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> get_paradise_default_channel(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">u8</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb36-34"><a href="#cb36-34" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;sources&quot;</span><span class="op">,</span> <span class="st">&quot;radio_paradise&quot;</span><span class="op">,</span> <span class="st">&quot;default_channel&quot;</span>]) <span class="op">{</span></span>
<span id="cb36-35"><a href="#cb36-35" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span><span class="dt">String</span>(s)) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb36-36"><a href="#cb36-36" aria-hidden="true" tabindex="-1"></a> <span class="co">// Accepter les noms conviviaux</span></span>
<span id="cb36-37"><a href="#cb36-37" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> s<span class="op">.</span>as_str() <span class="op">{</span></span>
<span id="cb36-38"><a href="#cb36-38" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;main&quot;</span> <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="dv">0</span>)<span class="op">,</span></span>
<span id="cb36-39"><a href="#cb36-39" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;mellow&quot;</span> <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="dv">1</span>)<span class="op">,</span></span>
<span id="cb36-40"><a href="#cb36-40" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;rock&quot;</span> <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="dv">2</span>)<span class="op">,</span></span>
<span id="cb36-41"><a href="#cb36-41" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;eclectic&quot;</span> <span class="op">=&gt;</span> <span class="cn">Ok</span>(<span class="dv">3</span>)<span class="op">,</span></span>
<span id="cb36-42"><a href="#cb36-42" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb36-43"><a href="#cb36-43" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_paradise_default_channel(<span class="dv">0</span>)<span class="op">?;</span></span>
<span id="cb36-44"><a href="#cb36-44" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="dv">0</span>)</span>
<span id="cb36-45"><a href="#cb36-45" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-46"><a href="#cb36-46" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-47"><a href="#cb36-47" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-48"><a href="#cb36-48" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="pp">Value::</span>Number(n)) <span class="cf">if</span> n<span class="op">.</span>is_u64() <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb36-49"><a href="#cb36-49" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> ch <span class="op">=</span> n<span class="op">.</span>as_u64()<span class="op">.</span>unwrap()<span class="op">;</span></span>
<span id="cb36-50"><a href="#cb36-50" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> ch <span class="op">&lt;=</span> <span class="dv">3</span> <span class="op">{</span></span>
<span id="cb36-51"><a href="#cb36-51" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(ch <span class="kw">as</span> <span class="dt">u8</span>)</span>
<span id="cb36-52"><a href="#cb36-52" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb36-53"><a href="#cb36-53" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_paradise_default_channel(<span class="dv">0</span>)<span class="op">?;</span></span>
<span id="cb36-54"><a href="#cb36-54" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="dv">0</span>)</span>
<span id="cb36-55"><a href="#cb36-55" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-56"><a href="#cb36-56" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-57"><a href="#cb36-57" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb36-58"><a href="#cb36-58" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb36-59"><a href="#cb36-59" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;sources&quot;</span><span class="op">,</span> <span class="st">&quot;radio_paradise&quot;</span><span class="op">,</span> <span class="st">&quot;default_channel&quot;</span>]<span class="op">,</span></span>
<span id="cb36-60"><a href="#cb36-60" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(<span class="st">&quot;main&quot;</span><span class="op">.</span>to_string())<span class="op">,</span></span>
<span id="cb36-61"><a href="#cb36-61" aria-hidden="true" tabindex="-1"></a> )<span class="op">?;</span></span>
<span id="cb36-62"><a href="#cb36-62" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(<span class="dv">0</span>)</span>
<span id="cb36-63"><a href="#cb36-63" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-64"><a href="#cb36-64" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-65"><a href="#cb36-65" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-66"><a href="#cb36-66" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-67"><a href="#cb36-67" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> set_paradise_default_channel(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> channel<span class="op">:</span> <span class="dt">u8</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb36-68"><a href="#cb36-68" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> name <span class="op">=</span> <span class="cf">match</span> channel <span class="op">{</span></span>
<span id="cb36-69"><a href="#cb36-69" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span> <span class="op">=&gt;</span> <span class="st">&quot;main&quot;</span><span class="op">,</span></span>
<span id="cb36-70"><a href="#cb36-70" aria-hidden="true" tabindex="-1"></a> <span class="dv">1</span> <span class="op">=&gt;</span> <span class="st">&quot;mellow&quot;</span><span class="op">,</span></span>
<span id="cb36-71"><a href="#cb36-71" aria-hidden="true" tabindex="-1"></a> <span class="dv">2</span> <span class="op">=&gt;</span> <span class="st">&quot;rock&quot;</span><span class="op">,</span></span>
<span id="cb36-72"><a href="#cb36-72" aria-hidden="true" tabindex="-1"></a> <span class="dv">3</span> <span class="op">=&gt;</span> <span class="st">&quot;eclectic&quot;</span><span class="op">,</span></span>
<span id="cb36-73"><a href="#cb36-73" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cf">return</span> <span class="cn">Err</span>(<span class="pp">anyhow::anyhow!</span>(<span class="st">&quot;Invalid channel ID: {}&quot;</span><span class="op">,</span> channel))<span class="op">,</span></span>
<span id="cb36-74"><a href="#cb36-74" aria-hidden="true" tabindex="-1"></a> <span class="op">};</span></span>
<span id="cb36-75"><a href="#cb36-75" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb36-76"><a href="#cb36-76" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;sources&quot;</span><span class="op">,</span> <span class="st">&quot;radio_paradise&quot;</span><span class="op">,</span> <span class="st">&quot;default_channel&quot;</span>]<span class="op">,</span></span>
<span id="cb36-77"><a href="#cb36-77" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(name<span class="op">.</span>to_string())<span class="op">,</span></span>
<span id="cb36-78"><a href="#cb36-78" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb36-79"><a href="#cb36-79" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb36-80"><a href="#cb36-80" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h2 id="intégration-dans-un-crate">Intégration dans un crate</h2>
<h3 id="structure-recommandée">Structure recommandée</h3>
<pre><code>pmoxxx/
├── Cargo.toml
├── src/
│ ├── lib.rs # Exporte le trait d&#39;extension
│ ├── config_ext.rs # Implémentation du trait
│ └── ... # Reste du code du crate</pre>
<h3 id="dans-cargo.toml">Dans Cargo.toml</h3>
<div class="sourceCode" id="cb38"><pre
class="sourceCode toml"><code class="sourceCode toml"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[dependencies]</span></span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a><span class="dt">pmoconfig</span> <span class="op">=</span> <span class="op">{ </span><span class="dt">path</span><span class="op"> =</span> <span class="st">&quot;../pmoconfig&quot;</span><span class="op"> }</span></span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a><span class="dt">anyhow</span> <span class="op">=</span> <span class="st">&quot;1.0&quot;</span></span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a><span class="dt">serde_yaml</span> <span class="op">=</span> <span class="st">&quot;0.9&quot;</span></span>
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Si c&#39;est un cache, inclure pmocache</span></span>
<span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a><span class="dt">pmocache</span> <span class="op">=</span> <span class="op">{ </span><span class="dt">path</span><span class="op"> =</span> <span class="st">&quot;../pmocache&quot;</span><span class="op">, </span><span class="dt">optional</span><span class="op"> =</span> <span class="cn">false</span><span class="op"> }</span></span></pre></div>
<h3 id="dans-lib.rs">Dans lib.rs</h3>
<div class="sourceCode" id="cb39"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Exporter le trait pour qu&#39;il soit utilisable</span></span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">mod</span> config_ext<span class="op">;</span></span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">use</span> <span class="pp">config_ext::</span>XxxConfigExt<span class="op">;</span></span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Le reste du code du crate</span></span>
<span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a><span class="co">// ...</span></span></pre></div>
<h3 id="utilisation-dans-le-code-applicatif">Utilisation dans le code
applicatif</h3>
<div class="sourceCode" id="cb40"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmoconfig::</span>get_config<span class="op">;</span></span>
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">pmoxxx::</span>XxxConfigExt<span class="op">;</span></span>
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-4"><a href="#cb40-4" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> main() <span class="op">-&gt;</span> <span class="pp">anyhow::</span><span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb40-5"><a href="#cb40-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> config <span class="op">=</span> get_config()<span class="op">;</span></span>
<span id="cb40-6"><a href="#cb40-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb40-7"><a href="#cb40-7" aria-hidden="true" tabindex="-1"></a> <span class="co">// Utiliser les méthodes du trait d&#39;extension</span></span>
<span id="cb40-8"><a href="#cb40-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> value <span class="op">=</span> config<span class="op">.</span>get_xxx_value()<span class="op">?;</span></span>
<span id="cb40-9"><a href="#cb40-9" aria-hidden="true" tabindex="-1"></a> config<span class="op">.</span>set_xxx_value(new_value)<span class="op">?;</span></span>
<span id="cb40-10"><a href="#cb40-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb40-11"><a href="#cb40-11" aria-hidden="true" tabindex="-1"></a> <span class="co">// Factory method</span></span>
<span id="cb40-12"><a href="#cb40-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> client <span class="op">=</span> config<span class="op">.</span>create_xxx_client()<span class="op">?;</span></span>
<span id="cb40-13"><a href="#cb40-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb40-14"><a href="#cb40-14" aria-hidden="true" tabindex="-1"></a> <span class="cn">Ok</span>(())</span>
<span id="cb40-15"><a href="#cb40-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<h2 id="checklist-pour-créer-un-nouveau-trait-dextension">Checklist pour
créer un nouveau trait dextension</h2>
<ul class="task-list">
<li><label><input type="checkbox" />Créer le fichier
<code>src/config_ext.rs</code> dans le crate</label></li>
<li><label><input type="checkbox" />Définir les constantes pour les
valeurs par défaut</label></li>
<li><label><input type="checkbox" />Créer le trait
<code>XxxConfigExt</code> avec documentation</label></li>
<li><label><input type="checkbox" />Implémenter les getters avec gestion
derreur appropriée</label></li>
<li><label><input type="checkbox" />Implémenter les setters</label></li>
<li><label><input type="checkbox" />Ajouter les helpers de validation si
nécessaire</label></li>
<li><label><input type="checkbox" />Ajouter les factory methods si
applicable</label></li>
<li><label><input type="checkbox" />Documenter chaque méthode avec
exemples</label></li>
<li><label><input type="checkbox" />Exporter le trait dans
<code>lib.rs</code></label></li>
<li><label><input type="checkbox" />Ajouter <code>pmoconfig</code> dans
<code>Cargo.toml</code></label></li>
<li><label><input type="checkbox" />Tester lintégration</label></li>
</ul>
<h2 id="philosophie-du-pattern">Philosophie du pattern</h2>
<h3 id="avantages">Avantages</h3>
<ol type="1">
<li><strong>Séparation des préoccupations</strong> : Chaque crate gère
sa propre configuration</li>
<li><strong>Type safety</strong> : Les erreurs de type sont détectées à
la compilation</li>
<li><strong>Extensibilité</strong> : Facile dajouter de nouveaux crates
sans modifier pmoconfig</li>
<li><strong>Cohérence</strong> : Pattern uniforme dans tout le
projet</li>
<li><strong>Documentation</strong> : Interface self-documenting avec
exemples</li>
</ol>
<h3 id="principes-directeurs">Principes directeurs</h3>
<ol type="1">
<li><strong>Minimalisme</strong> : Ne configurer que ce qui doit
vraiment lêtre</li>
<li><strong>Defaults intelligents</strong> : Valeurs par défaut sensées
et documentées</li>
<li><strong>Auto-persistence</strong> : Les valeurs importantes sont
persistées automatiquement</li>
<li><strong>User-friendly</strong> : Noms conviviaux dans le YAML
(strings au lieu de nombres)</li>
<li><strong>Fail-safe</strong> : Gestion des erreurs gracieuse avec
fallback sur défauts</li>
<li><strong>Zero surprise</strong> : Comportement prévisible et
cohérent</li>
</ol>
<h2 id="sécurité-chiffrement-des-mots-de-passe">Sécurité : Chiffrement
des mots de passe</h2>
<p>Tous les mots de passe dans la configuration doivent pouvoir être
chiffrés. Voir <code>pmoconfig/PASSWORD_ENCRYPTION.md</code> pour les
détails.</p>
<h3 id="pattern-pour-les-mots-de-passe">Pattern pour les mots de
passe</h3>
<div class="sourceCode" id="cb41"><pre
class="sourceCode rust"><code class="sourceCode rust"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_xxx_password(<span class="op">&amp;</span><span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="kw">self</span><span class="op">.</span>get_value(<span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;xxx&quot;</span><span class="op">,</span> <span class="st">&quot;password&quot;</span>])<span class="op">?</span> <span class="op">{</span></span>
<span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(s) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a> <span class="co">// Déchiffrement automatique</span></span>
<span id="cb41-5"><a href="#cb41-5" aria-hidden="true" tabindex="-1"></a> <span class="pp">pmoconfig::encryption::</span>get_password(<span class="op">&amp;</span>s)</span>
<span id="cb41-6"><a href="#cb41-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span>map_err(<span class="op">|</span>e<span class="op">|</span> <span class="pp">anyhow!</span>(<span class="st">&quot;Failed to decrypt password: {}&quot;</span><span class="op">,</span> e))</span>
<span id="cb41-7"><a href="#cb41-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb41-8"><a href="#cb41-8" aria-hidden="true" tabindex="-1"></a> _ <span class="op">=&gt;</span> <span class="cn">Err</span>(<span class="pp">anyhow!</span>(<span class="st">&quot;Password not configured&quot;</span>))<span class="op">,</span></span>
<span id="cb41-9"><a href="#cb41-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb41-10"><a href="#cb41-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb41-11"><a href="#cb41-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-12"><a href="#cb41-12" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> set_xxx_password(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> password<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb41-13"><a href="#cb41-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// Le chiffrement est fait manuellement par l&#39;utilisateur avec l&#39;outil</span></span>
<span id="cb41-14"><a href="#cb41-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">self</span><span class="op">.</span>set_value(</span>
<span id="cb41-15"><a href="#cb41-15" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span>[<span class="st">&quot;accounts&quot;</span><span class="op">,</span> <span class="st">&quot;xxx&quot;</span><span class="op">,</span> <span class="st">&quot;password&quot;</span>]<span class="op">,</span></span>
<span id="cb41-16"><a href="#cb41-16" aria-hidden="true" tabindex="-1"></a> <span class="pp">Value::</span><span class="dt">String</span>(password<span class="op">.</span>to_string())<span class="op">,</span></span>
<span id="cb41-17"><a href="#cb41-17" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb41-18"><a href="#cb41-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></pre></div>
<p><strong>Important</strong> : Le setter stocke le mot de passe tel
quel. Cest lutilisateur qui décide de le chiffrer ou non avec loutil
<code>encrypt_password</code>.</p>
<h2 id="références">Références</h2>
<h3 id="fichiers-dexemple-à-consulter">Fichiers dexemple à
consulter</h3>
<ul>
<li><strong>Cache générique</strong> :
<code>pmocache/src/config_ext.rs</code></li>
<li><strong>Cache spécialisé</strong> :
<code>pmocovers/src/config_ext.rs</code> ou
<code>pmoaudiocache/src/config_ext.rs</code></li>
<li><strong>Service avec auth</strong> :
<code>pmoqobuz/src/config_ext.rs</code></li>
<li><strong>Configuration minimale</strong> :
<code>pmoparadise/src/config_ext.rs</code></li>
<li><strong>Configuration UPnP</strong> :
<code>pmoupnp/src/config_ext.rs</code></li>
<li><strong>Chiffrement</strong> :
<code>pmoconfig/PASSWORD_ENCRYPTION.md</code></li>
</ul>
<h3 id="documentation-pmoconfig">Documentation pmoconfig</h3>
<ul>
<li><code>pmoconfig::Config::get_value()</code></li>
<li><code>pmoconfig::Config::set_value()</code></li>
<li><code>pmoconfig::Config::get_managed_dir()</code></li>
<li><code>pmoconfig::encryption</code> module</li>
</ul>
</article>
</body>
</html>