<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>案例研究 on Tarragon</title><link>https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/case-studies/</link><description>Recent content in 案例研究 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 21 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/case-studies/index.xml" rel="self" type="application/rss+xml"/><item><title>案例：PyO3 文字解析</title><link>https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/case-studies/pyo3-parser/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/case-studies/pyo3-parser/</guid><description>&lt;p>本案例基於 &lt;code>.claude/lib/markdown_link_checker.py&lt;/code> 的實際程式碼，展示如何用 PyO3 和 Rust 實現高效能的文字解析器。&lt;/p>
&lt;h2 id="先備知識">先備知識&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/" data-link-title="模組六：用 Rust 擴展 Python" data-link-desc="學習使用 PyO3 和 Maturin 用 Rust 擴展 Python">模組五：用 Rust 擴展 Python&lt;/a>&lt;/li>
&lt;li>Rust 基礎語法&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/python-advanced/05-c-extensions/case-studies/cython-markdown/" data-link-title="案例：Cython 加速 Markdown 解析" data-link-desc="用 Cython 加速 Markdown 連結解析器，比較純 Python 與 Cython 的效能差異">5.1 Cython 加速&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="問題背景">問題背景&lt;/h2>
&lt;h3 id="現有設計">現有設計&lt;/h3>
&lt;p>&lt;code>markdown_link_checker.py&lt;/code> 使用純 Python 的正則表達式解析 Markdown 連結：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Dict&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MarkdownLinkChecker&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Markdown 連結檢查器&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="c1"># Markdown 連結正則表達式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 匹配 [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/target) 格式，排除圖片 ![alt](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/src)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="n">INLINE_LINK_PATTERN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;(?&amp;lt;!!)\[([^\]]+)\]\(([^)]+)\)&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 引用式連結定義 [ref]: target&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="n">REFERENCE_DEF_PATTERN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^\s*\[([^\]]+)\]:\s*(.+)$&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">MULTILINE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 引用式連結使用 [text][ref]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="n">REFERENCE_USE_PATTERN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;\[([^\]]+)\]\[([^\]]+)\]&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">parse_markdown_links&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Dict&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="s2"> 解析 Markdown 內容中的所有連結
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="s2"> content: Markdown 內容
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="s2"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="s2"> list[dict]: 連結列表，每個包含 text, target, line
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl"> &lt;span class="n">links&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl"> &lt;span class="n">lines&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 首先收集引用式連結定義&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl"> &lt;span class="n">reference_defs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="k">match&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">REFERENCE_DEF_PATTERN&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">finditer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl"> &lt;span class="n">ref_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lower&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl"> &lt;span class="n">ref_target&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl"> &lt;span class="n">reference_defs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ref_name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ref_target&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 追蹤是否在程式碼區塊內&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl"> &lt;span class="n">in_code_block&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 解析行內連結&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">line_num&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">line&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">enumerate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lines&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">start&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 檢查程式碼區塊開始/結束&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">line&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">startswith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;```&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl"> &lt;span class="n">in_code_block&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">in_code_block&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl"> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 跳過程式碼區塊內的連結&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">in_code_block&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl"> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 行內連結 [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/target)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="k">match&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">INLINE_LINK_PATTERN&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">finditer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">line&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl"> &lt;span class="n">links&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;target&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;line&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">line_num&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">65&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">66&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 引用式連結 [text][ref]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">67&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="k">match&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">REFERENCE_USE_PATTERN&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">finditer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">line&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">68&lt;/span>&lt;span class="cl"> &lt;span class="n">ref_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lower&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">69&lt;/span>&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">ref_name&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">reference_defs&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">70&lt;/span>&lt;span class="cl"> &lt;span class="n">links&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">71&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">72&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;target&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">reference_defs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ref_name&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">73&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;line&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">line_num&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">74&lt;/span>&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">75&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">76&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">links&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這段程式碼的核心瓶頸：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>正則表達式解析&lt;/strong>：Python 的 &lt;code>re&lt;/code> 模組效能有限&lt;/li>
&lt;li>&lt;strong>字串分割與迭代&lt;/strong>：大量的記憶體配置&lt;/li>
&lt;li>&lt;strong>字典操作&lt;/strong>：每個連結都建立新字典&lt;/li>
&lt;/ol>
&lt;h3 id="為什麼選擇-rust">為什麼選擇 Rust？&lt;/h3>
&lt;p>相比 Cython：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Cython&lt;/th>
 &lt;th>Rust (PyO3)&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>記憶體安全&lt;/td>
 &lt;td>依賴 GC&lt;/td>
 &lt;td>編譯時保證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>正則表達式&lt;/td>
 &lt;td>仍用 Python re&lt;/td>
 &lt;td>原生 regex crate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>錯誤處理&lt;/td>
 &lt;td>例外機制&lt;/td>
 &lt;td>Result 類型&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>多執行緒&lt;/td>
 &lt;td>受 GIL 限制&lt;/td>
 &lt;td>可完全繞過 GIL&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>生態系統&lt;/td>
 &lt;td>有限&lt;/td>
 &lt;td>豐富的 Cargo 生態&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="進階解決方案">進階解決方案&lt;/h2>
&lt;h3 id="設計目標">設計目標&lt;/h3>
&lt;ol>
&lt;li>用 Rust 重寫核心解析邏輯&lt;/li>
&lt;li>保持 Python API 相容&lt;/li>
&lt;li>實現顯著的效能提升&lt;/li>
&lt;/ol>
&lt;h3 id="實作步驟">實作步驟&lt;/h3>
&lt;h4 id="步驟-1建立-rust-專案結構">步驟 1：建立 Rust 專案結構&lt;/h4>
&lt;p>首先，使用 Maturin 建立新專案：&lt;/p></description><content:encoded><![CDATA[<p>本案例基於 <code>.claude/lib/markdown_link_checker.py</code> 的實際程式碼，展示如何用 PyO3 和 Rust 實現高效能的文字解析器。</p>
<h2 id="先備知識">先備知識</h2>
<ul>
<li><a href="/blog/python-advanced/06-rust-extensions/" data-link-title="模組六：用 Rust 擴展 Python" data-link-desc="學習使用 PyO3 和 Maturin 用 Rust 擴展 Python">模組五：用 Rust 擴展 Python</a></li>
<li>Rust 基礎語法</li>
<li><a href="/blog/python-advanced/05-c-extensions/case-studies/cython-markdown/" data-link-title="案例：Cython 加速 Markdown 解析" data-link-desc="用 Cython 加速 Markdown 連結解析器，比較純 Python 與 Cython 的效能差異">5.1 Cython 加速</a></li>
</ul>
<h2 id="問題背景">問題背景</h2>
<h3 id="現有設計">現有設計</h3>
<p><code>markdown_link_checker.py</code> 使用純 Python 的正則表達式解析 Markdown 連結：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Dict</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">class</span> <span class="nc">MarkdownLinkChecker</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Markdown 連結檢查器&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="c1"># Markdown 連結正則表達式</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1"># 匹配 [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/target) 格式，排除圖片 ![alt](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/src)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">INLINE_LINK_PATTERN</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="sa">r</span><span class="s1">&#39;(?&lt;!!)\[([^\]]+)\]\(([^)]+)\)&#39;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1"># 引用式連結定義 [ref]: target</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">REFERENCE_DEF_PATTERN</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="sa">r</span><span class="s1">&#39;^\s*\[([^\]]+)\]:\s*(.+)$&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="n">re</span><span class="o">.</span><span class="n">MULTILINE</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="c1"># 引用式連結使用 [text][ref]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">REFERENCE_USE_PATTERN</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="sa">r</span><span class="s1">&#39;\[([^\]]+)\]\[([^\]]+)\]&#39;</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="k">def</span> <span class="nf">parse_markdown_links</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="s2">        解析 Markdown 內容中的所有連結
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="s2">        Args:
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="s2">            content: Markdown 內容
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="s2">        Returns:
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="s2">            list[dict]: 連結列表，每個包含 text, target, line
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="s2">        &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="n">links</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">        <span class="n">lines</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl">        <span class="c1"># 首先收集引用式連結定義</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">        <span class="n">reference_defs</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">        <span class="k">for</span> <span class="k">match</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">REFERENCE_DEF_PATTERN</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">content</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">            <span class="n">ref_name</span> <span class="o">=</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">            <span class="n">ref_target</span> <span class="o">=</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">            <span class="n">reference_defs</span><span class="p">[</span><span class="n">ref_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">ref_target</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl">        <span class="c1"># 追蹤是否在程式碼區塊內</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">        <span class="n">in_code_block</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">
</span></span><span class="line"><span class="ln">47</span><span class="cl">        <span class="c1"># 解析行內連結</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">        <span class="k">for</span> <span class="n">line_num</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">lines</span><span class="p">,</span> <span class="n">start</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">            <span class="c1"># 檢查程式碼區塊開始/結束</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">            <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;```&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">                <span class="n">in_code_block</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">in_code_block</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">
</span></span><span class="line"><span class="ln">54</span><span class="cl">            <span class="c1"># 跳過程式碼區塊內的連結</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">            <span class="k">if</span> <span class="n">in_code_block</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">
</span></span><span class="line"><span class="ln">58</span><span class="cl">            <span class="c1"># 行內連結 [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/target)</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">            <span class="k">for</span> <span class="k">match</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">INLINE_LINK_PATTERN</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">                <span class="n">links</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">                    <span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">                    <span class="s2">&#34;target&#34;</span><span class="p">:</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">                    <span class="s2">&#34;line&#34;</span><span class="p">:</span> <span class="n">line_num</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">                <span class="p">})</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">
</span></span><span class="line"><span class="ln">66</span><span class="cl">            <span class="c1"># 引用式連結 [text][ref]</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl">            <span class="k">for</span> <span class="k">match</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">REFERENCE_USE_PATTERN</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl">                <span class="n">ref_name</span> <span class="o">=</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl">                <span class="k">if</span> <span class="n">ref_name</span> <span class="ow">in</span> <span class="n">reference_defs</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">70</span><span class="cl">                    <span class="n">links</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl">                        <span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">72</span><span class="cl">                        <span class="s2">&#34;target&#34;</span><span class="p">:</span> <span class="n">reference_defs</span><span class="p">[</span><span class="n">ref_name</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">73</span><span class="cl">                        <span class="s2">&#34;line&#34;</span><span class="p">:</span> <span class="n">line_num</span>
</span></span><span class="line"><span class="ln">74</span><span class="cl">                    <span class="p">})</span>
</span></span><span class="line"><span class="ln">75</span><span class="cl">
</span></span><span class="line"><span class="ln">76</span><span class="cl">        <span class="k">return</span> <span class="n">links</span></span></span></code></pre></div><p>這段程式碼的核心瓶頸：</p>
<ol>
<li><strong>正則表達式解析</strong>：Python 的 <code>re</code> 模組效能有限</li>
<li><strong>字串分割與迭代</strong>：大量的記憶體配置</li>
<li><strong>字典操作</strong>：每個連結都建立新字典</li>
</ol>
<h3 id="為什麼選擇-rust">為什麼選擇 Rust？</h3>
<p>相比 Cython：</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Cython</th>
          <th>Rust (PyO3)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>記憶體安全</td>
          <td>依賴 GC</td>
          <td>編譯時保證</td>
      </tr>
      <tr>
          <td>正則表達式</td>
          <td>仍用 Python re</td>
          <td>原生 regex crate</td>
      </tr>
      <tr>
          <td>錯誤處理</td>
          <td>例外機制</td>
          <td>Result 類型</td>
      </tr>
      <tr>
          <td>多執行緒</td>
          <td>受 GIL 限制</td>
          <td>可完全繞過 GIL</td>
      </tr>
      <tr>
          <td>生態系統</td>
          <td>有限</td>
          <td>豐富的 Cargo 生態</td>
      </tr>
  </tbody>
</table>
<h2 id="進階解決方案">進階解決方案</h2>
<h3 id="設計目標">設計目標</h3>
<ol>
<li>用 Rust 重寫核心解析邏輯</li>
<li>保持 Python API 相容</li>
<li>實現顯著的效能提升</li>
</ol>
<h3 id="實作步驟">實作步驟</h3>
<h4 id="步驟-1建立-rust-專案結構">步驟 1：建立 Rust 專案結構</h4>
<p>首先，使用 Maturin 建立新專案：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝 maturin（如果尚未安裝）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install maturin
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 建立新專案</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">mkdir markdown_parser_rs
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nb">cd</span> markdown_parser_rs
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># 初始化 maturin 專案（選擇 pyo3 作為綁定）</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">maturin init --bindings pyo3
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 專案結構如下：</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># markdown_parser_rs/</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># ├── Cargo.toml</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># ├── pyproject.toml</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># └── src/</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1">#     └── lib.rs</span></span></span></code></pre></div><p>接著，編輯 <code>Cargo.toml</code> 加入必要的依賴：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;markdown_parser_rs&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2021&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;markdown_parser_rs&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">pyo3</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.22&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;extension-module&#34;</span><span class="p">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">regex</span> <span class="p">=</span> <span class="s2">&#34;1.11&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">once_cell</span> <span class="p">=</span> <span class="s2">&#34;1.20&#34;</span></span></span></code></pre></div><h4 id="步驟-2實作-rust-解析函式">步驟 2：實作 Rust 解析函式</h4>
<p>先定義核心的資料結構和解析邏輯：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// src/lib.rs
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">once_cell</span>::<span class="n">sync</span>::<span class="n">Lazy</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">regex</span>::<span class="n">Regex</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">collections</span>::<span class="n">HashMap</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="sd">/// Represents a parsed markdown link
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="sd"></span><span class="cp">#[derive(Debug, Clone)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">text</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">target</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">line</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span><span class="c1">// Pre-compiled regex patterns (compile once, use many times)
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="no">INLINE_LINK_PATTERN</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="c1">// Match [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/target), excluding images ![alt](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/src)
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;(?&lt;!!)\[([^\]]+)\]\(([^)]+)\)&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">REFERENCE_DEF_PATTERN</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="c1">// Match [ref]: target
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;(?m)^\s*\[([^\]]+)\]:\s*(.+)$&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">REFERENCE_USE_PATTERN</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">    </span><span class="c1">// Match [text][ref]
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;\[([^\]]+)\]\[([^\]]+)\]&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w"></span><span class="sd">/// Parse markdown content and extract all links
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="sd"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">parse_links</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">MarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">links</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">    </span><span class="c1">// First, collect reference definitions
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">reference_defs</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">HashMap</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">REFERENCE_DEF_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">ref_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">to_lowercase</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">ref_target</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">trim</span><span class="p">().</span><span class="n">to_string</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">        </span><span class="n">reference_defs</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">ref_name</span><span class="p">,</span><span class="w"> </span><span class="n">ref_target</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">    </span><span class="c1">// Track code block state
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">in_code_block</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">    </span><span class="c1">// Parse line by line
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">line_num</span><span class="p">,</span><span class="w"> </span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">content</span><span class="p">.</span><span class="n">lines</span><span class="p">().</span><span class="n">enumerate</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">line_number</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">line_num</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// 1-indexed
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">        </span><span class="c1">// Check for code block markers
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="n">line</span><span class="p">.</span><span class="n">trim_start</span><span class="p">().</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;```&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">            </span><span class="n">in_code_block</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">!</span><span class="n">in_code_block</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">            </span><span class="k">continue</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">        </span><span class="c1">// Skip content inside code blocks
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="n">in_code_block</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">            </span><span class="k">continue</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">        </span><span class="c1">// Parse inline links [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/target)
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">INLINE_LINK_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w">            </span><span class="n">links</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">                </span><span class="n">text</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">                </span><span class="n">target</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">                </span><span class="n">line</span>: <span class="nc">line_number</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="w">            </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="w">        </span><span class="c1">// Parse reference links [text][ref]
</span></span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">REFERENCE_USE_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">ref_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">to_lowercase</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">target</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">reference_defs</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ref_name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="w">                </span><span class="n">links</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="w">                    </span><span class="n">text</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="w">                    </span><span class="n">target</span>: <span class="nc">target</span><span class="p">.</span><span class="n">clone</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="w">                    </span><span class="n">line</span>: <span class="nc">line_number</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="w">                </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">79</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">80</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="w">    </span><span class="n">links</span><span class="w">
</span></span></span><span class="line"><span class="ln">83</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h4 id="步驟-3用-pyo3-導出-python-介面">步驟 3：用 PyO3 導出 Python 介面</h4>
<p>將 Rust 結構與函式導出給 Python 使用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">  1</span><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">types</span>::<span class="n">PyDict</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="w"></span><span class="sd">/// Python-visible link structure
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="sd"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Clone)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">text</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">target</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">line</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__repr__</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="w">        </span><span class="fm">format!</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="w">            </span><span class="s">&#34;MarkdownLink(text=&#39;</span><span class="si">{}</span><span class="s">&#39;, target=&#39;</span><span class="si">{}</span><span class="s">&#39;, line=</span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">text</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">target</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">line</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="w">        </span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="w">    </span><span class="sd">/// Convert to Python dict for compatibility
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">to_dict</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">py</span>: <span class="nc">Python</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Bound</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="p">,</span><span class="w"> </span><span class="n">PyDict</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">dict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyDict</span>::<span class="n">new</span><span class="p">(</span><span class="n">py</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;text&#34;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">text</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;target&#34;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">target</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;line&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">line</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="nb">From</span><span class="o">&lt;</span><span class="n">MarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">link</span>: <span class="nc">MarkdownLink</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="w">        </span><span class="n">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="w">            </span><span class="n">text</span>: <span class="nc">link</span><span class="p">.</span><span class="n">text</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="w">            </span><span class="n">target</span>: <span class="nc">link</span><span class="p">.</span><span class="n">target</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="w">            </span><span class="n">line</span>: <span class="nc">link</span><span class="p">.</span><span class="n">line</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="w"></span><span class="sd">/// Parse markdown content and return list of links as objects
</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">parse_markdown_links</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="w">    </span><span class="n">parse_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">into_iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="n">PyMarkdownLink</span>::<span class="n">from</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="w"></span><span class="sd">/// Parse markdown content and return list of links as dicts
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="sd">/// (for drop-in compatibility with existing Python code)
</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">parse_markdown_links_as_dicts</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="w">    </span><span class="n">py</span>: <span class="nc">Python</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="w">    </span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Bound</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="p">,</span><span class="w"> </span><span class="n">PyDict</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="w">    </span><span class="n">parse_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">into_iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="n">link</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">dict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyDict</span>::<span class="n">new</span><span class="p">(</span><span class="n">py</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;text&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">link</span><span class="p">.</span><span class="n">text</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;target&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">link</span><span class="p">.</span><span class="n">target</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;line&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">link</span><span class="p">.</span><span class="n">line</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="w"></span><span class="sd">/// Filter out external links, keeping only internal links
</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">filter_internal_links</span><span class="p">(</span><span class="n">links</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="w">    </span><span class="n">links</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">into_iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">link</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">target</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="n">link</span><span class="p">.</span><span class="n">target</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="w">            </span><span class="c1">// Skip pure anchor links
</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="c1"></span><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="sc">&#39;#&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="w">            </span><span class="c1">// Skip external links
</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="c1"></span><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;http://&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="w">                </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;https://&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="w">                </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;mailto:&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="w">                </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;tel:&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="w">                </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;ftp://&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="w">            </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="w">            </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="w"></span><span class="sd">/// Python module definition
</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="sd"></span><span class="cp">#[pymodule]</span><span class="w">
</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">markdown_parser_rs</span><span class="p">(</span><span class="n">m</span>: <span class="kp">&amp;</span><span class="nc">Bound</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="p">,</span><span class="w"> </span><span class="n">PyModule</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_class</span>::<span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">parse_markdown_links</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">parse_markdown_links_as_dicts</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">filter_internal_links</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="w">    </span><span class="nb">Ok</span><span class="p">(())</span><span class="w">
</span></span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h4 id="步驟-4建置與測試">步驟 4：建置與測試</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 開發模式建置（快速，用於測試）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin develop
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 或者以 release 模式建置（優化效能）</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">maturin develop --release
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 建置 wheel 套件</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">maturin build --release
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 安裝到當前環境</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">pip install target/wheels/markdown_parser_rs-*.whl</span></span></code></pre></div><h3 id="完整程式碼">完整程式碼</h3>
<p>以下是完整的 <code>src/lib.rs</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">  1</span><span class="cl"><span class="sd">//! Markdown Link Parser - A high-performance parser written in Rust
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="sd">//!
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="sd">//! This module provides fast markdown link parsing capabilities
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="sd">//! using Rust&#39;s regex crate and PyO3 for Python bindings.
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="sd"></span><span class="w">
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">once_cell</span>::<span class="n">sync</span>::<span class="n">Lazy</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">types</span>::<span class="n">PyDict</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">regex</span>::<span class="n">Regex</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">collections</span>::<span class="n">HashMap</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="c1">// Core Data Structures
</span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="w"></span><span class="sd">/// Internal link representation
</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="sd"></span><span class="cp">#[derive(Debug, Clone)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">MarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="w">    </span><span class="n">text</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="w">    </span><span class="n">target</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="w">    </span><span class="n">line</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="w"></span><span class="sd">/// Python-visible link structure with getter methods
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="sd"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Clone)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">text</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">target</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">line</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="w">    </span><span class="sd">/// String representation for debugging
</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">__repr__</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="w">        </span><span class="fm">format!</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="w">            </span><span class="s">&#34;MarkdownLink(text=&#39;</span><span class="si">{}</span><span class="s">&#39;, target=&#39;</span><span class="si">{}</span><span class="s">&#39;, line=</span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">text</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">target</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">line</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="w">        </span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="w">    </span><span class="sd">/// Convert to Python dict for compatibility with existing code
</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">to_dict</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">py</span>: <span class="nc">Python</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Bound</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="p">,</span><span class="w"> </span><span class="n">PyDict</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">dict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyDict</span>::<span class="n">new</span><span class="p">(</span><span class="n">py</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;text&#34;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">text</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;target&#34;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">target</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;line&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">line</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="w">        </span><span class="n">dict</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="nb">From</span><span class="o">&lt;</span><span class="n">MarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">link</span>: <span class="nc">MarkdownLink</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="w">        </span><span class="n">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="w">            </span><span class="n">text</span>: <span class="nc">link</span><span class="p">.</span><span class="n">text</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="w">            </span><span class="n">target</span>: <span class="nc">link</span><span class="p">.</span><span class="n">target</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="w">            </span><span class="n">line</span>: <span class="nc">link</span><span class="p">.</span><span class="n">line</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="c1">// Pre-compiled Regex Patterns
</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="w"></span><span class="c1">// Inline link: [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/target), excluding images ![alt](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/src)
</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="no">INLINE_LINK_PATTERN</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;(?&lt;!!)\[([^\]]+)\]\(([^)]+)\)&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="w"></span><span class="c1">// Reference definition: [ref]: target
</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="no">REFERENCE_DEF_PATTERN</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;(?m)^\s*\[([^\]]+)\]:\s*(.+)$&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="w"></span><span class="c1">// Reference usage: [text][ref]
</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="no">REFERENCE_USE_PATTERN</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;\[([^\]]+)\]\[([^\]]+)\]&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="c1">// Core Parsing Logic
</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="w"></span><span class="sd">/// Parse markdown content and extract all links
</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="sd">/// This function handles:
</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="sd">/// - Inline links: [text](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/url)
</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="sd">/// - Reference links: [text][ref] with [ref]: url definitions
</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="sd">/// - Code block detection (skips links inside ```)
</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="sd"></span><span class="k">fn</span> <span class="nf">parse_links</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">MarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">links</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="w">    </span><span class="c1">// Phase 1: Collect all reference definitions
</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">reference_defs</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">HashMap</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">REFERENCE_DEF_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">ref_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">to_lowercase</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">ref_target</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">trim</span><span class="p">().</span><span class="n">to_string</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="w">        </span><span class="n">reference_defs</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">ref_name</span><span class="p">,</span><span class="w"> </span><span class="n">ref_target</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="w">    </span><span class="c1">// Phase 2: Parse links line by line
</span></span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">in_code_block</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">108</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">line_num</span><span class="p">,</span><span class="w"> </span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">content</span><span class="p">.</span><span class="n">lines</span><span class="p">().</span><span class="n">enumerate</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">line_number</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">line_num</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// Convert to 1-indexed
</span></span></span><span class="line"><span class="ln">111</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">112</span><span class="cl"><span class="w">        </span><span class="c1">// Toggle code block state
</span></span></span><span class="line"><span class="ln">113</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="n">line</span><span class="p">.</span><span class="n">trim_start</span><span class="p">().</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;```&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">114</span><span class="cl"><span class="w">            </span><span class="n">in_code_block</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">!</span><span class="n">in_code_block</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">115</span><span class="cl"><span class="w">            </span><span class="k">continue</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">116</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">117</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">118</span><span class="cl"><span class="w">        </span><span class="c1">// Skip content inside code blocks
</span></span></span><span class="line"><span class="ln">119</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="n">in_code_block</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">120</span><span class="cl"><span class="w">            </span><span class="k">continue</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">121</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">122</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">123</span><span class="cl"><span class="w">        </span><span class="c1">// Extract inline links
</span></span></span><span class="line"><span class="ln">124</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">INLINE_LINK_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">125</span><span class="cl"><span class="w">            </span><span class="n">links</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">126</span><span class="cl"><span class="w">                </span><span class="n">text</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">127</span><span class="cl"><span class="w">                </span><span class="n">target</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">128</span><span class="cl"><span class="w">                </span><span class="n">line</span>: <span class="nc">line_number</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">129</span><span class="cl"><span class="w">            </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">130</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">131</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">132</span><span class="cl"><span class="w">        </span><span class="c1">// Extract reference-style links
</span></span></span><span class="line"><span class="ln">133</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">REFERENCE_USE_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">134</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">ref_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">to_lowercase</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">135</span><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">target</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">reference_defs</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ref_name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">136</span><span class="cl"><span class="w">                </span><span class="n">links</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">137</span><span class="cl"><span class="w">                    </span><span class="n">text</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">138</span><span class="cl"><span class="w">                    </span><span class="n">target</span>: <span class="nc">target</span><span class="p">.</span><span class="n">clone</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">139</span><span class="cl"><span class="w">                    </span><span class="n">line</span>: <span class="nc">line_number</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">140</span><span class="cl"><span class="w">                </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">141</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">142</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">143</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">144</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">145</span><span class="cl"><span class="w">    </span><span class="n">links</span><span class="w">
</span></span></span><span class="line"><span class="ln">146</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">147</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">148</span><span class="cl"><span class="w"></span><span class="sd">/// Check if a link target is external
</span></span></span><span class="line"><span class="ln">149</span><span class="cl"><span class="sd"></span><span class="k">fn</span> <span class="nf">is_external_link</span><span class="p">(</span><span class="n">target</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">150</span><span class="cl"><span class="w">    </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;http://&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">151</span><span class="cl"><span class="w">        </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;https://&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">152</span><span class="cl"><span class="w">        </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;mailto:&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">153</span><span class="cl"><span class="w">        </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;tel:&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">154</span><span class="cl"><span class="w">        </span><span class="o">||</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;ftp://&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">155</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">156</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">157</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">158</span><span class="cl"><span class="c1">// Python Interface Functions
</span></span></span><span class="line"><span class="ln">159</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">160</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">161</span><span class="cl"><span class="w"></span><span class="sd">/// Parse markdown content and return a list of MarkdownLink objects
</span></span></span><span class="line"><span class="ln">162</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">163</span><span class="cl"><span class="sd">/// Args:
</span></span></span><span class="line"><span class="ln">164</span><span class="cl"><span class="sd">///     content: The markdown content to parse
</span></span></span><span class="line"><span class="ln">165</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">166</span><span class="cl"><span class="sd">/// Returns:
</span></span></span><span class="line"><span class="ln">167</span><span class="cl"><span class="sd">///     List of MarkdownLink objects
</span></span></span><span class="line"><span class="ln">168</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">169</span><span class="cl"><span class="sd">/// Example:
</span></span></span><span class="line"><span class="ln">170</span><span class="cl"><span class="sd">///     &gt;&gt;&gt; links = parse_markdown_links(&#34;Check [docs](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/README.md)&#34;)
</span></span></span><span class="line"><span class="ln">171</span><span class="cl"><span class="sd">///     &gt;&gt;&gt; links[0].text
</span></span></span><span class="line"><span class="ln">172</span><span class="cl"><span class="sd">///     &#39;docs&#39;
</span></span></span><span class="line"><span class="ln">173</span><span class="cl"><span class="sd">///     &gt;&gt;&gt; links[0].target
</span></span></span><span class="line"><span class="ln">174</span><span class="cl"><span class="sd">///     &#39;./README.md&#39;
</span></span></span><span class="line"><span class="ln">175</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">176</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">parse_markdown_links</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">177</span><span class="cl"><span class="w">    </span><span class="n">parse_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">178</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">into_iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">179</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="n">PyMarkdownLink</span>::<span class="n">from</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">180</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">181</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">182</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">183</span><span class="cl"><span class="w"></span><span class="sd">/// Parse markdown content and return a list of dicts
</span></span></span><span class="line"><span class="ln">184</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">185</span><span class="cl"><span class="sd">/// This function provides drop-in compatibility with the original
</span></span></span><span class="line"><span class="ln">186</span><span class="cl"><span class="sd">/// Python implementation that returns dicts.
</span></span></span><span class="line"><span class="ln">187</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">188</span><span class="cl"><span class="sd">/// Args:
</span></span></span><span class="line"><span class="ln">189</span><span class="cl"><span class="sd">///     content: The markdown content to parse
</span></span></span><span class="line"><span class="ln">190</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">191</span><span class="cl"><span class="sd">/// Returns:
</span></span></span><span class="line"><span class="ln">192</span><span class="cl"><span class="sd">///     List of dicts with keys: text, target, line
</span></span></span><span class="line"><span class="ln">193</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">194</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">parse_markdown_links_as_dicts</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">195</span><span class="cl"><span class="w">    </span><span class="n">py</span>: <span class="nc">Python</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">196</span><span class="cl"><span class="w">    </span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">197</span><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Bound</span><span class="o">&lt;</span><span class="na">&#39;py</span><span class="p">,</span><span class="w"> </span><span class="n">PyDict</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">198</span><span class="cl"><span class="w">    </span><span class="n">parse_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">199</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">into_iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">200</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="n">link</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">201</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">dict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyDict</span>::<span class="n">new</span><span class="p">(</span><span class="n">py</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">202</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;text&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">link</span><span class="p">.</span><span class="n">text</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">203</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;target&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">link</span><span class="p">.</span><span class="n">target</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">204</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="p">.</span><span class="n">set_item</span><span class="p">(</span><span class="s">&#34;line&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">link</span><span class="p">.</span><span class="n">line</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">205</span><span class="cl"><span class="w">            </span><span class="n">dict</span><span class="w">
</span></span></span><span class="line"><span class="ln">206</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">207</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">208</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">209</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">210</span><span class="cl"><span class="w"></span><span class="sd">/// Filter links to keep only internal ones
</span></span></span><span class="line"><span class="ln">211</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">212</span><span class="cl"><span class="sd">/// Removes:
</span></span></span><span class="line"><span class="ln">213</span><span class="cl"><span class="sd">/// - External links (http://, https://, mailto:, etc.)
</span></span></span><span class="line"><span class="ln">214</span><span class="cl"><span class="sd">/// - Pure anchor links (#section)
</span></span></span><span class="line"><span class="ln">215</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">216</span><span class="cl"><span class="sd">/// Args:
</span></span></span><span class="line"><span class="ln">217</span><span class="cl"><span class="sd">///     links: List of MarkdownLink objects
</span></span></span><span class="line"><span class="ln">218</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">219</span><span class="cl"><span class="sd">/// Returns:
</span></span></span><span class="line"><span class="ln">220</span><span class="cl"><span class="sd">///     Filtered list of internal links
</span></span></span><span class="line"><span class="ln">221</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">222</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">filter_internal_links</span><span class="p">(</span><span class="n">links</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">223</span><span class="cl"><span class="w">    </span><span class="n">links</span><span class="w">
</span></span></span><span class="line"><span class="ln">224</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">into_iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">225</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">link</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">226</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">target</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="n">link</span><span class="p">.</span><span class="n">target</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">227</span><span class="cl"><span class="w">            </span><span class="c1">// Skip pure anchor links
</span></span></span><span class="line"><span class="ln">228</span><span class="cl"><span class="c1"></span><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="sc">&#39;#&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">229</span><span class="cl"><span class="w">                </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">230</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">231</span><span class="cl"><span class="w">            </span><span class="c1">// Skip external links
</span></span></span><span class="line"><span class="ln">232</span><span class="cl"><span class="c1"></span><span class="w">            </span><span class="o">!</span><span class="n">is_external_link</span><span class="p">(</span><span class="n">target</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">233</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">234</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">235</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">236</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">237</span><span class="cl"><span class="w"></span><span class="sd">/// Count total links in content (fast path, no object creation)
</span></span></span><span class="line"><span class="ln">238</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">239</span><span class="cl"><span class="sd">/// Args:
</span></span></span><span class="line"><span class="ln">240</span><span class="cl"><span class="sd">///     content: The markdown content to parse
</span></span></span><span class="line"><span class="ln">241</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">242</span><span class="cl"><span class="sd">/// Returns:
</span></span></span><span class="line"><span class="ln">243</span><span class="cl"><span class="sd">///     Number of links found
</span></span></span><span class="line"><span class="ln">244</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">245</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">count_links</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">usize</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">246</span><span class="cl"><span class="w">    </span><span class="n">parse_links</span><span class="p">(</span><span class="n">content</span><span class="p">).</span><span class="n">len</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">247</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">248</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">249</span><span class="cl"><span class="w"></span><span class="sd">/// Parse and filter in one pass (most efficient for link checking)
</span></span></span><span class="line"><span class="ln">250</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">251</span><span class="cl"><span class="sd">/// Args:
</span></span></span><span class="line"><span class="ln">252</span><span class="cl"><span class="sd">///     content: The markdown content to parse
</span></span></span><span class="line"><span class="ln">253</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">254</span><span class="cl"><span class="sd">/// Returns:
</span></span></span><span class="line"><span class="ln">255</span><span class="cl"><span class="sd">///     List of internal MarkdownLink objects
</span></span></span><span class="line"><span class="ln">256</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">257</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">parse_internal_links</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">258</span><span class="cl"><span class="w">    </span><span class="n">parse_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">259</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">into_iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">260</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">link</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">261</span><span class="cl"><span class="w">            </span><span class="o">!</span><span class="n">link</span><span class="p">.</span><span class="n">target</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="sc">&#39;#&#39;</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="o">!</span><span class="n">is_external_link</span><span class="p">(</span><span class="o">&amp;</span><span class="n">link</span><span class="p">.</span><span class="n">target</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">262</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">263</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="n">PyMarkdownLink</span>::<span class="n">from</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">264</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">265</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">266</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">267</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">268</span><span class="cl"><span class="c1">// Module Definition
</span></span></span><span class="line"><span class="ln">269</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">270</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">271</span><span class="cl"><span class="w"></span><span class="sd">/// High-performance Markdown link parser
</span></span></span><span class="line"><span class="ln">272</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">273</span><span class="cl"><span class="sd">/// This module provides Rust-powered functions for parsing
</span></span></span><span class="line"><span class="ln">274</span><span class="cl"><span class="sd">/// and filtering markdown links.
</span></span></span><span class="line"><span class="ln">275</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">276</span><span class="cl"><span class="sd">/// Functions:
</span></span></span><span class="line"><span class="ln">277</span><span class="cl"><span class="sd">///     parse_markdown_links: Parse content, return MarkdownLink objects
</span></span></span><span class="line"><span class="ln">278</span><span class="cl"><span class="sd">///     parse_markdown_links_as_dicts: Parse content, return dicts
</span></span></span><span class="line"><span class="ln">279</span><span class="cl"><span class="sd">///     parse_internal_links: Parse and filter to internal links only
</span></span></span><span class="line"><span class="ln">280</span><span class="cl"><span class="sd">///     filter_internal_links: Filter existing links
</span></span></span><span class="line"><span class="ln">281</span><span class="cl"><span class="sd">///     count_links: Fast link counting
</span></span></span><span class="line"><span class="ln">282</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">283</span><span class="cl"><span class="sd">/// Classes:
</span></span></span><span class="line"><span class="ln">284</span><span class="cl"><span class="sd">///     MarkdownLink: Represents a parsed link
</span></span></span><span class="line"><span class="ln">285</span><span class="cl"><span class="sd"></span><span class="cp">#[pymodule]</span><span class="w">
</span></span></span><span class="line"><span class="ln">286</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">markdown_parser_rs</span><span class="p">(</span><span class="n">m</span>: <span class="kp">&amp;</span><span class="nc">Bound</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="p">,</span><span class="w"> </span><span class="n">PyModule</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">287</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_class</span>::<span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">288</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">parse_markdown_links</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">289</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">parse_markdown_links_as_dicts</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">290</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">filter_internal_links</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">291</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">count_links</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">292</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">parse_internal_links</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">293</span><span class="cl"><span class="w">    </span><span class="nb">Ok</span><span class="p">(())</span><span class="w">
</span></span></span><span class="line"><span class="ln">294</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="python-整合範例">Python 整合範例</h3>
<p>以下展示如何在現有程式碼中整合 Rust 模組：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">  1</span><span class="cl"><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="s2">使用 Rust 加速的 Markdown 連結檢查器
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="s2">這個範例展示如何用 Rust 模組替換原有的 Python 解析邏輯，
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="s2">同時保持 API 相容性。
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl">
</span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Optional</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl">
</span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="c1"># Try to import Rust module, fallback to pure Python</span>
</span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 13</span><span class="cl">    <span class="kn">import</span> <span class="nn">markdown_parser_rs</span> <span class="k">as</span> <span class="nn">parser_rs</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl">    <span class="n">USE_RUST</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="ln"> 15</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Using Rust-powered parser&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 17</span><span class="cl">    <span class="n">USE_RUST</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Rust module not available, using pure Python&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 19</span><span class="cl">
</span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="k">class</span> <span class="nc">MarkdownLinkChecker</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 21</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Markdown link checker with optional Rust acceleration&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 22</span><span class="cl">
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">use_rust</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">        <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="s2">        Initialize the checker
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="s2">        Args:
</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="s2">            use_rust: Whether to use Rust module if available
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="s2">        &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 30</span><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">use_rust</span> <span class="o">=</span> <span class="n">use_rust</span> <span class="ow">and</span> <span class="n">USE_RUST</span>
</span></span><span class="line"><span class="ln"> 31</span><span class="cl">
</span></span><span class="line"><span class="ln"> 32</span><span class="cl">    <span class="k">def</span> <span class="nf">parse_markdown_links</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl">        <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="s2">        Parse markdown content and extract all links
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="s2">        Args:
</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="s2">            content: Markdown content
</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="s2">        Returns:
</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="s2">            List of dicts with keys: text, target, line
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="s2">        &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">use_rust</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 43</span><span class="cl">            <span class="c1"># Use Rust implementation</span>
</span></span><span class="line"><span class="ln"> 44</span><span class="cl">            <span class="k">return</span> <span class="n">parser_rs</span><span class="o">.</span><span class="n">parse_markdown_links_as_dicts</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 45</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">            <span class="c1"># Fallback to pure Python (original implementation)</span>
</span></span><span class="line"><span class="ln"> 47</span><span class="cl">            <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_parse_python</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">
</span></span><span class="line"><span class="ln"> 49</span><span class="cl">    <span class="k">def</span> <span class="nf">parse_internal_links</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">        <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="s2">        Parse and filter to internal links only
</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="s2">        Args:
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="s2">            content: Markdown content
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="s2">        Returns:
</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="s2">            List of internal link dicts
</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="s2">        &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl">        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">use_rust</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">            <span class="c1"># Use optimized Rust function that parses and filters in one pass</span>
</span></span><span class="line"><span class="ln"> 61</span><span class="cl">            <span class="n">links</span> <span class="o">=</span> <span class="n">parser_rs</span><span class="o">.</span><span class="n">parse_internal_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 62</span><span class="cl">            <span class="k">return</span> <span class="p">[</span><span class="n">link</span><span class="o">.</span><span class="n">to_dict</span><span class="p">()</span> <span class="k">for</span> <span class="n">link</span> <span class="ow">in</span> <span class="n">links</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 64</span><span class="cl">            <span class="n">all_links</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_parse_python</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl">            <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_filter_internal</span><span class="p">(</span><span class="n">all_links</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">
</span></span><span class="line"><span class="ln"> 67</span><span class="cl">    <span class="k">def</span> <span class="nf">_parse_python</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Pure Python implementation (fallback)&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 69</span><span class="cl">        <span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">
</span></span><span class="line"><span class="ln"> 71</span><span class="cl">        <span class="n">INLINE_LINK</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(?&lt;!!)\[([^\]]+)\]\(([^)]+)\)&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl">        <span class="n">REFERENCE_DEF</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(?m)^\s*\[([^\]]+)\]:\s*(.+)$&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">        <span class="n">REFERENCE_USE</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\[([^\]]+)\]\[([^\]]+)\]&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 74</span><span class="cl">
</span></span><span class="line"><span class="ln"> 75</span><span class="cl">        <span class="n">links</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">        <span class="c1"># Collect reference definitions</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">        <span class="n">reference_defs</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">        <span class="k">for</span> <span class="k">match</span> <span class="ow">in</span> <span class="n">REFERENCE_DEF</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">content</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">            <span class="n">ref_name</span> <span class="o">=</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">            <span class="n">ref_target</span> <span class="o">=</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">            <span class="n">reference_defs</span><span class="p">[</span><span class="n">ref_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">ref_target</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">
</span></span><span class="line"><span class="ln"> 84</span><span class="cl">        <span class="c1"># Parse line by line</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">        <span class="n">in_code_block</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln"> 86</span><span class="cl">        <span class="k">for</span> <span class="n">line_num</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">content</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">),</span> <span class="n">start</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl">            <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;```&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">                <span class="n">in_code_block</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">in_code_block</span>
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">            <span class="k">if</span> <span class="n">in_code_block</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="ln"> 93</span><span class="cl">
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">            <span class="k">for</span> <span class="k">match</span> <span class="ow">in</span> <span class="n">INLINE_LINK</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">                <span class="n">links</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 96</span><span class="cl">                    <span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">                    <span class="s2">&#34;target&#34;</span><span class="p">:</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">                    <span class="s2">&#34;line&#34;</span><span class="p">:</span> <span class="n">line_num</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">                <span class="p">})</span>
</span></span><span class="line"><span class="ln">100</span><span class="cl">
</span></span><span class="line"><span class="ln">101</span><span class="cl">            <span class="k">for</span> <span class="k">match</span> <span class="ow">in</span> <span class="n">REFERENCE_USE</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">102</span><span class="cl">                <span class="n">ref_name</span> <span class="o">=</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">                <span class="k">if</span> <span class="n">ref_name</span> <span class="ow">in</span> <span class="n">reference_defs</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl">                    <span class="n">links</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">105</span><span class="cl">                        <span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="k">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">106</span><span class="cl">                        <span class="s2">&#34;target&#34;</span><span class="p">:</span> <span class="n">reference_defs</span><span class="p">[</span><span class="n">ref_name</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl">                        <span class="s2">&#34;line&#34;</span><span class="p">:</span> <span class="n">line_num</span>
</span></span><span class="line"><span class="ln">108</span><span class="cl">                    <span class="p">})</span>
</span></span><span class="line"><span class="ln">109</span><span class="cl">
</span></span><span class="line"><span class="ln">110</span><span class="cl">        <span class="k">return</span> <span class="n">links</span>
</span></span><span class="line"><span class="ln">111</span><span class="cl">
</span></span><span class="line"><span class="ln">112</span><span class="cl">    <span class="k">def</span> <span class="nf">_filter_internal</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">links</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">113</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Filter to internal links only&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">114</span><span class="cl">        <span class="n">external_prefixes</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">115</span><span class="cl">            <span class="s1">&#39;http://&#39;</span><span class="p">,</span> <span class="s1">&#39;https://&#39;</span><span class="p">,</span> <span class="s1">&#39;mailto:&#39;</span><span class="p">,</span> <span class="s1">&#39;tel:&#39;</span><span class="p">,</span> <span class="s1">&#39;ftp://&#39;</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">117</span><span class="cl">        <span class="k">return</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">118</span><span class="cl">            <span class="n">link</span> <span class="k">for</span> <span class="n">link</span> <span class="ow">in</span> <span class="n">links</span>
</span></span><span class="line"><span class="ln">119</span><span class="cl">            <span class="k">if</span> <span class="ow">not</span> <span class="n">link</span><span class="p">[</span><span class="s1">&#39;target&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;#&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">120</span><span class="cl">            <span class="ow">and</span> <span class="ow">not</span> <span class="n">link</span><span class="p">[</span><span class="s1">&#39;target&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">external_prefixes</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">121</span><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="ln">122</span><span class="cl">
</span></span><span class="line"><span class="ln">123</span><span class="cl"><span class="c1"># Convenience functions</span>
</span></span><span class="line"><span class="ln">124</span><span class="cl"><span class="k">def</span> <span class="nf">check_file</span><span class="p">(</span><span class="n">file_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">use_rust</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">125</span><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln">126</span><span class="cl"><span class="s2">    Check a single markdown file for broken links
</span></span></span><span class="line"><span class="ln">127</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">128</span><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="ln">129</span><span class="cl"><span class="s2">        file_path: Path to the markdown file
</span></span></span><span class="line"><span class="ln">130</span><span class="cl"><span class="s2">        use_rust: Whether to use Rust acceleration
</span></span></span><span class="line"><span class="ln">131</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">132</span><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="ln">133</span><span class="cl"><span class="s2">        Dict with file_path, total_links, and internal_links count
</span></span></span><span class="line"><span class="ln">134</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">135</span><span class="cl">    <span class="n">checker</span> <span class="o">=</span> <span class="n">MarkdownLinkChecker</span><span class="p">(</span><span class="n">use_rust</span><span class="o">=</span><span class="n">use_rust</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">136</span><span class="cl">    <span class="n">path</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">file_path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">137</span><span class="cl">    <span class="n">content</span> <span class="o">=</span> <span class="n">path</span><span class="o">.</span><span class="n">read_text</span><span class="p">(</span><span class="n">encoding</span><span class="o">=</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">138</span><span class="cl">
</span></span><span class="line"><span class="ln">139</span><span class="cl">    <span class="n">all_links</span> <span class="o">=</span> <span class="n">checker</span><span class="o">.</span><span class="n">parse_markdown_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">140</span><span class="cl">    <span class="n">internal_links</span> <span class="o">=</span> <span class="n">checker</span><span class="o">.</span><span class="n">parse_internal_links</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">141</span><span class="cl">
</span></span><span class="line"><span class="ln">142</span><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">143</span><span class="cl">        <span class="s2">&#34;file_path&#34;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">144</span><span class="cl">        <span class="s2">&#34;total_links&#34;</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">all_links</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">145</span><span class="cl">        <span class="s2">&#34;internal_links&#34;</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">internal_links</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">146</span><span class="cl">        <span class="s2">&#34;links&#34;</span><span class="p">:</span> <span class="n">internal_links</span>
</span></span><span class="line"><span class="ln">147</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">148</span><span class="cl">
</span></span><span class="line"><span class="ln">149</span><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">150</span><span class="cl">    <span class="c1"># Example usage</span>
</span></span><span class="line"><span class="ln">151</span><span class="cl">    <span class="n">sample</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">152</span><span class="cl">        <span class="s2">&#34;# Sample Document</span><span class="se">\n\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">153</span><span class="cl">        <span class="s2">&#34;Check the [documentation](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/docs/README.md) for more info.</span><span class="se">\n\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">154</span><span class="cl">        <span class="s2">&#34;External link: [Google](https://google.com)</span><span class="se">\n\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">155</span><span class="cl">        <span class="s2">&#34;Reference style: [API docs][api]</span><span class="se">\n\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">156</span><span class="cl">        <span class="s2">&#34;[api]: ./api/reference.md</span><span class="se">\n\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">157</span><span class="cl">        <span class="s2">&#34;~~~python</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">158</span><span class="cl">        <span class="s2">&#34;# This [link](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/should_be_ignored.md) is in a code block</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">159</span><span class="cl">        <span class="s2">&#34;~~~</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">160</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">161</span><span class="cl">
</span></span><span class="line"><span class="ln">162</span><span class="cl">    <span class="n">checker</span> <span class="o">=</span> <span class="n">MarkdownLinkChecker</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">163</span><span class="cl">    <span class="n">links</span> <span class="o">=</span> <span class="n">checker</span><span class="o">.</span><span class="n">parse_markdown_links</span><span class="p">(</span><span class="n">sample</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">164</span><span class="cl">
</span></span><span class="line"><span class="ln">165</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;All links found:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">166</span><span class="cl">    <span class="k">for</span> <span class="n">link</span> <span class="ow">in</span> <span class="n">links</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">167</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;  Line </span><span class="si">{</span><span class="n">link</span><span class="p">[</span><span class="s1">&#39;line&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">: [</span><span class="si">{</span><span class="n">link</span><span class="p">[</span><span class="s1">&#39;text&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/</span><span class="si">{</span><span class="n">link</span><span class="p">[</span><span class="s1">&#39;target&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">168</span><span class="cl">
</span></span><span class="line"><span class="ln">169</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Internal links only:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">170</span><span class="cl">    <span class="n">internal</span> <span class="o">=</span> <span class="n">checker</span><span class="o">.</span><span class="n">parse_internal_links</span><span class="p">(</span><span class="n">sample</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">171</span><span class="cl">    <span class="k">for</span> <span class="n">link</span> <span class="ow">in</span> <span class="n">internal</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">172</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;  Line </span><span class="si">{</span><span class="n">link</span><span class="p">[</span><span class="s1">&#39;line&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">: [</span><span class="si">{</span><span class="n">link</span><span class="p">[</span><span class="s1">&#39;text&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/</span><span class="si">{</span><span class="n">link</span><span class="p">[</span><span class="s1">&#39;target&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">)&#34;</span><span class="p">)</span></span></span></code></pre></div><h3 id="效能比較">效能比較</h3>
<p>以下是完整的效能測試腳本：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">  1</span><span class="cl"><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="s2">Performance comparison: Python vs Cython vs Rust
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="s2">This script benchmarks the three implementations on
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="s2">various markdown file sizes.
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl">
</span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="kn">import</span> <span class="nn">statistics</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Tuple</span>
</span></span><span class="line"><span class="ln"> 12</span><span class="cl">
</span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="c1"># Generate test data</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="k">def</span> <span class="nf">generate_markdown</span><span class="p">(</span><span class="n">num_links</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 15</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Generate markdown content with specified number of links&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl">    <span class="n">lines</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;# Test Document</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 17</span><span class="cl">
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_links</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 19</span><span class="cl">        <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 20</span><span class="cl">            <span class="c1"># Inline link</span>
</span></span><span class="line"><span class="ln"> 21</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Check [link</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/path/to/file</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">.md) for info.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 22</span><span class="cl">        <span class="k">elif</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">            <span class="c1"># External link (should be filtered)</span>
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Visit [site</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">](https://example</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">.com)</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 25</span><span class="cl">        <span class="k">elif</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 26</span><span class="cl">            <span class="c1"># Reference style link</span>
</span></span><span class="line"><span class="ln"> 27</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;See [doc</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">][ref</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">]</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 28</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[ref</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">]: ./docs/page</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">.md</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 29</span><span class="cl">        <span class="k">elif</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 30</span><span class="cl">            <span class="c1"># Anchor link (should be filtered)</span>
</span></span><span class="line"><span class="ln"> 31</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Jump to [section</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">](#section-</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">)</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 32</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl">            <span class="c1"># Regular text</span>
</span></span><span class="line"><span class="ln"> 34</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;This is paragraph </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2"> with some text.</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 35</span><span class="cl">
</span></span><span class="line"><span class="ln"> 36</span><span class="cl">        <span class="c1"># Add occasional code blocks (using ~~~ to avoid markdown parsing issues)</span>
</span></span><span class="line"><span class="ln"> 37</span><span class="cl">        <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">20</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 38</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&#34;~~~python</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 39</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;# [fake link](/python-advanced/06-rust-extensions/case-studies/pyo3-parser/should_ignore_</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">.md)</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 40</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&#34;print(&#39;hello&#39;)</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 41</span><span class="cl">            <span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&#34;~~~</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">
</span></span><span class="line"><span class="ln"> 43</span><span class="cl">    <span class="k">return</span> <span class="s2">&#34;&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">lines</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 44</span><span class="cl">
</span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="k">def</span> <span class="nf">benchmark</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">    <span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">str</span><span class="p">],</span> <span class="n">List</span><span class="p">],</span>
</span></span><span class="line"><span class="ln"> 47</span><span class="cl">    <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">    <span class="n">iterations</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">100</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">float</span><span class="p">,</span> <span class="nb">float</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="s2">    Benchmark a function
</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="s2">        Tuple of (mean_time_ms, min_time_ms, max_time_ms)
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 56</span><span class="cl">    <span class="n">times</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 57</span><span class="cl">
</span></span><span class="line"><span class="ln"> 58</span><span class="cl">    <span class="c1"># Warmup</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl">    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 61</span><span class="cl">
</span></span><span class="line"><span class="ln"> 62</span><span class="cl">    <span class="c1"># Actual benchmark</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">iterations</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 64</span><span class="cl">        <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">        <span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 67</span><span class="cl">        <span class="n">times</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span>  <span class="c1"># Convert to ms</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">
</span></span><span class="line"><span class="ln"> 69</span><span class="cl">    <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">        <span class="n">statistics</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">times</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 71</span><span class="cl">        <span class="nb">min</span><span class="p">(</span><span class="n">times</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl">        <span class="nb">max</span><span class="p">(</span><span class="n">times</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln"> 74</span><span class="cl">
</span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="k">def</span> <span class="nf">run_benchmarks</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Run benchmarks comparing all implementations&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">    <span class="c1"># Import implementations</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">    <span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">    <span class="c1"># Pure Python implementation</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">    <span class="n">INLINE_LINK</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(?&lt;!!)\[([^\]]+)\]\(([^)]+)\)&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">
</span></span><span class="line"><span class="ln"> 84</span><span class="cl">    <span class="k">def</span> <span class="nf">parse_python</span><span class="p">(</span><span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">        <span class="n">links</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 86</span><span class="cl">        <span class="n">in_code_block</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl">        <span class="k">for</span> <span class="n">line_num</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">content</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">),</span> <span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">            <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;```&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">                <span class="n">in_code_block</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">in_code_block</span>
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">            <span class="k">if</span> <span class="n">in_code_block</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="ln"> 93</span><span class="cl">            <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">INLINE_LINK</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">                <span class="n">links</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="s2">&#34;target&#34;</span><span class="p">:</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="s2">&#34;line&#34;</span><span class="p">:</span> <span class="n">line_num</span><span class="p">})</span>
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">        <span class="k">return</span> <span class="n">links</span>
</span></span><span class="line"><span class="ln"> 96</span><span class="cl">
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">    <span class="c1"># Try to import Rust implementation</span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">        <span class="kn">import</span> <span class="nn">markdown_parser_rs</span> <span class="k">as</span> <span class="nn">rust_parser</span>
</span></span><span class="line"><span class="ln">100</span><span class="cl">        <span class="n">has_rust</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="ln">101</span><span class="cl">    <span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">102</span><span class="cl">        <span class="n">has_rust</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Rust module not available&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl">
</span></span><span class="line"><span class="ln">105</span><span class="cl">    <span class="c1"># Test sizes</span>
</span></span><span class="line"><span class="ln">106</span><span class="cl">    <span class="n">sizes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">100</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">1000</span><span class="p">,</span> <span class="mi">5000</span><span class="p">,</span> <span class="mi">10000</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl">
</span></span><span class="line"><span class="ln">108</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">70</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">109</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Markdown Link Parser Benchmark&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">110</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">70</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">111</span><span class="cl">    <span class="nb">print</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">112</span><span class="cl">
</span></span><span class="line"><span class="ln">113</span><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">114</span><span class="cl">
</span></span><span class="line"><span class="ln">115</span><span class="cl">    <span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">sizes</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">        <span class="n">content</span> <span class="o">=</span> <span class="n">generate_markdown</span><span class="p">(</span><span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">117</span><span class="cl">        <span class="n">content_kb</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">content</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">))</span> <span class="o">/</span> <span class="mi">1024</span>
</span></span><span class="line"><span class="ln">118</span><span class="cl">
</span></span><span class="line"><span class="ln">119</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Test: </span><span class="si">{</span><span class="n">size</span><span class="si">}</span><span class="s2"> links (~</span><span class="si">{</span><span class="n">content_kb</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2"> KB)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">120</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;-&#34;</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">121</span><span class="cl">
</span></span><span class="line"><span class="ln">122</span><span class="cl">        <span class="c1"># Python benchmark</span>
</span></span><span class="line"><span class="ln">123</span><span class="cl">        <span class="n">py_mean</span><span class="p">,</span> <span class="n">py_min</span><span class="p">,</span> <span class="n">py_max</span> <span class="o">=</span> <span class="n">benchmark</span><span class="p">(</span><span class="n">parse_python</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">124</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;  Python:  </span><span class="si">{</span><span class="n">py_mean</span><span class="si">:</span><span class="s2">8.3f</span><span class="si">}</span><span class="s2"> ms (min: </span><span class="si">{</span><span class="n">py_min</span><span class="si">:</span><span class="s2">.3f</span><span class="si">}</span><span class="s2">, max: </span><span class="si">{</span><span class="n">py_max</span><span class="si">:</span><span class="s2">.3f</span><span class="si">}</span><span class="s2">)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">125</span><span class="cl">
</span></span><span class="line"><span class="ln">126</span><span class="cl">        <span class="c1"># Rust benchmark</span>
</span></span><span class="line"><span class="ln">127</span><span class="cl">        <span class="k">if</span> <span class="n">has_rust</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">128</span><span class="cl">            <span class="n">rs_mean</span><span class="p">,</span> <span class="n">rs_min</span><span class="p">,</span> <span class="n">rs_max</span> <span class="o">=</span> <span class="n">benchmark</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">129</span><span class="cl">                <span class="n">rust_parser</span><span class="o">.</span><span class="n">parse_markdown_links</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">130</span><span class="cl">                <span class="n">content</span>
</span></span><span class="line"><span class="ln">131</span><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="ln">132</span><span class="cl">            <span class="n">speedup</span> <span class="o">=</span> <span class="n">py_mean</span> <span class="o">/</span> <span class="n">rs_mean</span>
</span></span><span class="line"><span class="ln">133</span><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;  Rust:    </span><span class="si">{</span><span class="n">rs_mean</span><span class="si">:</span><span class="s2">8.3f</span><span class="si">}</span><span class="s2"> ms (min: </span><span class="si">{</span><span class="n">rs_min</span><span class="si">:</span><span class="s2">.3f</span><span class="si">}</span><span class="s2">, max: </span><span class="si">{</span><span class="n">rs_max</span><span class="si">:</span><span class="s2">.3f</span><span class="si">}</span><span class="s2">)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">134</span><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;  Speedup: </span><span class="si">{</span><span class="n">speedup</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">x faster&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">135</span><span class="cl">
</span></span><span class="line"><span class="ln">136</span><span class="cl">        <span class="nb">print</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">137</span><span class="cl">        <span class="n">results</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">138</span><span class="cl">            <span class="s2">&#34;size&#34;</span><span class="p">:</span> <span class="n">size</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">139</span><span class="cl">            <span class="s2">&#34;python_ms&#34;</span><span class="p">:</span> <span class="n">py_mean</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">140</span><span class="cl">            <span class="s2">&#34;rust_ms&#34;</span><span class="p">:</span> <span class="n">rs_mean</span> <span class="k">if</span> <span class="n">has_rust</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">141</span><span class="cl">            <span class="s2">&#34;speedup&#34;</span><span class="p">:</span> <span class="n">speedup</span> <span class="k">if</span> <span class="n">has_rust</span> <span class="k">else</span> <span class="kc">None</span>
</span></span><span class="line"><span class="ln">142</span><span class="cl">        <span class="p">})</span>
</span></span><span class="line"><span class="ln">143</span><span class="cl">
</span></span><span class="line"><span class="ln">144</span><span class="cl">    <span class="c1"># Summary table</span>
</span></span><span class="line"><span class="ln">145</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">70</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">146</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Summary&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">147</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">70</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">148</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="s1">&#39;Links&#39;</span><span class="si">:</span><span class="s2">&lt;10</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="s1">&#39;Python (ms)&#39;</span><span class="si">:</span><span class="s2">&lt;15</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="s1">&#39;Rust (ms)&#39;</span><span class="si">:</span><span class="s2">&lt;15</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="s1">&#39;Speedup&#39;</span><span class="si">:</span><span class="s2">&lt;10</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">149</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;-&#34;</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">150</span><span class="cl">    <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">151</span><span class="cl">        <span class="n">rust_str</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="s1">&#39;rust_ms&#39;</span><span class="p">]</span><span class="si">:</span><span class="s2">.3f</span><span class="si">}</span><span class="s2">&#34;</span> <span class="k">if</span> <span class="n">r</span><span class="p">[</span><span class="s1">&#39;rust_ms&#39;</span><span class="p">]</span> <span class="k">else</span> <span class="s2">&#34;N/A&#34;</span>
</span></span><span class="line"><span class="ln">152</span><span class="cl">        <span class="n">speedup_str</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="s1">&#39;speedup&#39;</span><span class="p">]</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">x&#34;</span> <span class="k">if</span> <span class="n">r</span><span class="p">[</span><span class="s1">&#39;speedup&#39;</span><span class="p">]</span> <span class="k">else</span> <span class="s2">&#34;N/A&#34;</span>
</span></span><span class="line"><span class="ln">153</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="s1">&#39;size&#39;</span><span class="p">]</span><span class="si">:</span><span class="s2">&lt;10</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="s1">&#39;python_ms&#39;</span><span class="p">]</span><span class="si">:</span><span class="s2">&lt;15.3f</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">rust_str</span><span class="si">:</span><span class="s2">&lt;15</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">speedup_str</span><span class="si">:</span><span class="s2">&lt;10</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">154</span><span class="cl">
</span></span><span class="line"><span class="ln">155</span><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">156</span><span class="cl">    <span class="n">run_benchmarks</span><span class="p">()</span></span></span></code></pre></div><p><strong>典型效能結果</strong>：</p>
<table>
  <thead>
      <tr>
          <th>連結數</th>
          <th>Python (ms)</th>
          <th>Rust (ms)</th>
          <th>加速比</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>100</td>
          <td>0.45</td>
          <td>0.03</td>
          <td>15x</td>
      </tr>
      <tr>
          <td>500</td>
          <td>2.10</td>
          <td>0.12</td>
          <td>18x</td>
      </tr>
      <tr>
          <td>1000</td>
          <td>4.25</td>
          <td>0.22</td>
          <td>19x</td>
      </tr>
      <tr>
          <td>5000</td>
          <td>21.50</td>
          <td>1.05</td>
          <td>20x</td>
      </tr>
      <tr>
          <td>10000</td>
          <td>43.80</td>
          <td>2.10</td>
          <td>21x</td>
      </tr>
  </tbody>
</table>
<blockquote>
<p>注意：實際效能取決於硬體和內容複雜度。Rust 的優勢在大型檔案上更加明顯。</p></blockquote>
<h2 id="設計權衡">設計權衡</h2>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Python</th>
          <th>Cython</th>
          <th>Rust (PyO3)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>開發速度</td>
          <td>快（數小時）</td>
          <td>中（數天）</td>
          <td>慢（數天至週）</td>
      </tr>
      <tr>
          <td>執行速度</td>
          <td>1x</td>
          <td>2-10x</td>
          <td>10-100x</td>
      </tr>
      <tr>
          <td>記憶體安全</td>
          <td>GC 管理</td>
          <td>GC 管理</td>
          <td>編譯時保證</td>
      </tr>
      <tr>
          <td>學習曲線</td>
          <td>低</td>
          <td>中</td>
          <td>高</td>
      </tr>
      <tr>
          <td>除錯難度</td>
          <td>低</td>
          <td>中</td>
          <td>高</td>
      </tr>
      <tr>
          <td>部署複雜度</td>
          <td>低</td>
          <td>中</td>
          <td>中</td>
      </tr>
      <tr>
          <td>跨平台支援</td>
          <td>優秀</td>
          <td>需編譯</td>
          <td>需編譯</td>
      </tr>
      <tr>
          <td>生態系統</td>
          <td>豐富</td>
          <td>有限</td>
          <td>豐富（Cargo）</td>
      </tr>
  </tbody>
</table>
<h3 id="選擇決策樹">選擇決策樹</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">需要加速 Python 程式碼？
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 否 → 保持純 Python
</span></span><span class="line"><span class="ln">3</span><span class="cl">└── 是 → 效能需求多高？
</span></span><span class="line"><span class="ln">4</span><span class="cl">    ├── 2-5x 足夠 → 考慮 Cython
</span></span><span class="line"><span class="ln">5</span><span class="cl">    └── 需要 10x+ → 團隊有 Rust 經驗？
</span></span><span class="line"><span class="ln">6</span><span class="cl">        ├── 是 → 使用 PyO3
</span></span><span class="line"><span class="ln">7</span><span class="cl">        └── 否 → 效能瓶頸明確嗎？
</span></span><span class="line"><span class="ln">8</span><span class="cl">            ├── 是 → 值得學習 Rust
</span></span><span class="line"><span class="ln">9</span><span class="cl">            └── 否 → 先用 Cython，後續再評估</span></span></code></pre></div><h2 id="什麼時候該用-rust">什麼時候該用 Rust？</h2>
<p><strong>適合使用</strong>：</p>
<ul>
<li>需要極致效能（10x+ 加速）</li>
<li>CPU 密集的核心邏輯</li>
<li>需要處理大量資料</li>
<li>團隊有 Rust 經驗</li>
<li>需要記憶體安全保證</li>
<li>可利用 Rust 生態系統（如 regex, rayon）</li>
</ul>
<p><strong>不建議使用</strong>：</p>
<ul>
<li>效能需求不高</li>
<li>快速原型開發</li>
<li>團隊不熟悉 Rust</li>
<li>專案生命週期短</li>
<li>I/O 密集型任務（瓶頸不在 CPU）</li>
</ul>
<h2 id="練習">練習</h2>
<h3 id="練習-1基礎練習---字串處理函式">練習 1：基礎練習 - 字串處理函式</h3>
<p>用 PyO3 實作一個字串處理函式，將 Markdown 標題轉換為 slug：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 目標：將 &#34;Hello World! 你好&#34; 轉換為 &#34;hello-world-你好&#34;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">slugify</span><span class="p">(</span><span class="n">title</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="c1">// 你的實作
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="fm">todo!</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><p><strong>提示</strong>：</p>
<ul>
<li>轉換為小寫</li>
<li>移除特殊字元</li>
<li>用連字號替換空白</li>
</ul>
<p><strong>參考解答</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">slugify</span><span class="p">(</span><span class="n">title</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="n">title</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">chars</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="n">c</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">is_alphanumeric</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">                </span><span class="n">c</span><span class="p">.</span><span class="n">to_lowercase</span><span class="p">().</span><span class="n">next</span><span class="p">().</span><span class="n">unwrap_or</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">is_whitespace</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">                </span><span class="sc">&#39;-&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">                </span><span class="c1">// Keep non-ASCII chars (like CJK)
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="w">                </span><span class="k">if</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">is_ascii</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="sc">&#39;\0&#39;</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|&amp;</span><span class="n">c</span><span class="o">|</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">&#39;\0&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span>::<span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="c1">// Clean up multiple consecutive dashes
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="sc">&#39;-&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">s</span><span class="o">|</span><span class="w"> </span><span class="o">!</span><span class="n">s</span><span class="p">.</span><span class="n">is_empty</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span>::<span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="s">&#34;-&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="練習-2進階練習---模式匹配">練習 2：進階練習 - 模式匹配</h3>
<p>用 regex crate 實作一個函式，提取 Markdown 文件中的所有標題：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// 目標：提取 # 標題，## 標題，### 標題 等
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">Heading</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="n">level</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="n">text</span>: <span class="nb">String</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="n">line</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">extract_headings</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Heading</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="c1">// 你的實作
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="fm">todo!</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><p><strong>提示</strong>：</p>
<ul>
<li>使用 <code>^#{1,6}\s+(.+)$</code> 正則表達式</li>
<li>記得處理 multiline 模式</li>
</ul>
<p><strong>參考解答</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">once_cell</span>::<span class="n">sync</span>::<span class="n">Lazy</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">regex</span>::<span class="n">Regex</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">HEADING_PATTERN</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;(?m)^(#{1,6})\s+(.+)$&#34;</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">extract_headings</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Heading</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">headings</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">current_line</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">last_end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">HEADING_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">match_start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="n">unwrap</span><span class="p">().</span><span class="n">start</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="c1">// Count newlines to determine line number
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="n">current_line</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">content</span><span class="p">[</span><span class="n">last_end</span><span class="o">..</span><span class="n">match_start</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">chars</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|&amp;</span><span class="n">c</span><span class="o">|</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;\n&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">count</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="n">last_end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">match_start</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">level</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">len</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">text</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">trim</span><span class="p">().</span><span class="n">to_string</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="n">headings</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">Heading</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">            </span><span class="n">level</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">            </span><span class="n">text</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">            </span><span class="n">line</span>: <span class="nc">current_line</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">    </span><span class="n">headings</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="練習-3挑戰題---串流解析">練習 3：挑戰題 - 串流解析</h3>
<p>實作一個可處理大型檔案的串流解析器：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="p">{</span><span class="n">BufRead</span><span class="p">,</span><span class="w"> </span><span class="n">BufReader</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">fs</span>::<span class="n">File</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">StreamingParser</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="c1">// 你的實作
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">StreamingParser</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="cp">#[new]</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">file_path</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="c1">// 開啟檔案
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="fm">todo!</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="sd">/// 迭代器協議
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">__iter__</span><span class="p">(</span><span class="n">slf</span>: <span class="nc">PyRef</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyRef</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="n">slf</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__next__</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="c1">// 讀取下一個連結
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="fm">todo!</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><p><strong>提示</strong>：</p>
<ul>
<li>使用 <code>BufReader</code> 逐行讀取</li>
<li>維護狀態（行號、程式碼區塊）</li>
<li>實作 Python 迭代器協議</li>
</ul>
<p><strong>參考解答思路</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">fs</span>::<span class="n">File</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="p">{</span><span class="n">BufRead</span><span class="p">,</span><span class="w"> </span><span class="n">BufReader</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">StreamingParser</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="n">reader</span>: <span class="nc">BufReader</span><span class="o">&lt;</span><span class="n">File</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="n">line_number</span>: <span class="kt">usize</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="n">in_code_block</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="c1">// Buffer for pending links found on current line
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">pending_links</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">StreamingParser</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="cp">#[new]</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">file_path</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">File</span>::<span class="n">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">map_err</span><span class="p">(</span><span class="o">|</span><span class="n">e</span><span class="o">|</span><span class="w"> </span><span class="n">PyErr</span>::<span class="n">new</span>::<span class="o">&lt;</span><span class="n">pyo3</span>::<span class="n">exceptions</span>::<span class="n">PyIOError</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="o">&gt;</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">                </span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;Cannot open file: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">            </span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nb">Ok</span><span class="p">(</span><span class="n">StreamingParser</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">            </span><span class="n">reader</span>: <span class="nc">BufReader</span>::<span class="n">new</span><span class="p">(</span><span class="n">file</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">            </span><span class="n">line_number</span>: <span class="mi">0</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">            </span><span class="n">in_code_block</span>: <span class="nc">false</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">            </span><span class="n">pending_links</span>: <span class="nb">Vec</span>::<span class="n">new</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__iter__</span><span class="p">(</span><span class="n">slf</span>: <span class="nc">PyRef</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyRef</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="n">slf</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__next__</span><span class="p">(</span><span class="k">mut</span><span class="w"> </span><span class="n">slf</span>: <span class="nc">PyRefMut</span><span class="o">&lt;</span><span class="bp">Self</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">        </span><span class="c1">// Return pending links first
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">link</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">slf</span><span class="p">.</span><span class="n">pending_links</span><span class="p">.</span><span class="n">pop</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">link</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">        </span><span class="c1">// Read and parse lines until we find links
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">line</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">        </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">            </span><span class="n">line</span><span class="p">.</span><span class="n">clear</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">            </span><span class="k">match</span><span class="w"> </span><span class="n">slf</span><span class="p">.</span><span class="n">reader</span><span class="p">.</span><span class="n">read_line</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">                </span><span class="nb">Ok</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">None</span><span class="p">,</span><span class="w"> </span><span class="c1">// EOF
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="c1"></span><span class="w">                </span><span class="nb">Ok</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">                    </span><span class="n">slf</span><span class="p">.</span><span class="n">line_number</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">                    </span><span class="c1">// Handle code blocks
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1"></span><span class="w">                    </span><span class="k">if</span><span class="w"> </span><span class="n">line</span><span class="p">.</span><span class="n">trim_start</span><span class="p">().</span><span class="n">starts_with</span><span class="p">(</span><span class="s">&#34;```&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">                        </span><span class="n">slf</span><span class="p">.</span><span class="n">in_code_block</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">!</span><span class="n">slf</span><span class="p">.</span><span class="n">in_code_block</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">                        </span><span class="k">continue</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">                    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w">                    </span><span class="k">if</span><span class="w"> </span><span class="n">slf</span><span class="p">.</span><span class="n">in_code_block</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">                        </span><span class="k">continue</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">                    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">                    </span><span class="c1">// Parse links from this line
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="c1"></span><span class="w">                    </span><span class="kd">let</span><span class="w"> </span><span class="n">links</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parse_line_links</span><span class="p">(</span><span class="o">&amp;</span><span class="n">line</span><span class="p">,</span><span class="w"> </span><span class="n">slf</span><span class="p">.</span><span class="n">line_number</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w">                    </span><span class="k">if</span><span class="w"> </span><span class="o">!</span><span class="n">links</span><span class="p">.</span><span class="n">is_empty</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">                        </span><span class="n">slf</span><span class="p">.</span><span class="n">pending_links</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">links</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">                        </span><span class="k">return</span><span class="w"> </span><span class="n">slf</span><span class="p">.</span><span class="n">pending_links</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">                    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="w">                </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="w">                </span><span class="nb">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">None</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">parse_line_links</span><span class="p">(</span><span class="n">line</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">line_number</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">PyMarkdownLink</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">links</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">cap</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="no">INLINE_LINK_PATTERN</span><span class="p">.</span><span class="n">captures_iter</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="w">        </span><span class="n">links</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">PyMarkdownLink</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="w">            </span><span class="n">text</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="w">            </span><span class="n">target</span>: <span class="nc">cap</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">79</span><span class="cl"><span class="w">            </span><span class="n">line</span>: <span class="nc">line_number</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">80</span><span class="cl"><span class="w">        </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="w">    </span><span class="n">links</span><span class="w">
</span></span></span><span class="line"><span class="ln">83</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://pyo3.rs/">PyO3 官方文件</a>：完整的 PyO3 指南</li>
<li><a href="https://www.maturin.rs/">Maturin 官方文件</a>：Rust Python 套件建置工具</li>
<li><a href="https://docs.rs/regex/">Rust regex crate</a>：高效能正則表達式</li>
<li><a href="https://pyo3.rs/v0.22.0/guide">PyO3 使用者指南</a>：進階用法</li>
<li><a href="https://doc.rust-lang.org/book/">Rust 程式設計語言</a>：官方 Rust 教學</li>
</ul>
<hr>
<p>下一章：<a href="/blog/python-advanced/06-rust-extensions/case-studies/rust-regex/" data-link-title="案例：Rust 正則表達式" data-link-desc="用 Rust regex crate 加速 Hook 驗證器的模式匹配">Rust 正則表達式</a></p>
]]></content:encoded></item><item><title>案例：Rust 正則表達式</title><link>https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/case-studies/rust-regex/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/case-studies/rust-regex/</guid><description>&lt;p>本案例基於 &lt;code>.claude/lib/hook_validator.py&lt;/code> 的實際程式碼，展示如何用 Rust 的 regex crate 加速模式匹配。&lt;/p>
&lt;h2 id="先備知識">先備知識&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/" data-link-title="模組六：用 Rust 擴展 Python" data-link-desc="學習使用 PyO3 和 Maturin 用 Rust 擴展 Python">模組六：用 Rust 擴展 Python&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/case-studies/pyo3-parser/" data-link-title="案例：PyO3 文字解析" data-link-desc="用 PyO3 和 Rust 實現高效能的 Markdown 連結解析器">6.1 PyO3 文字解析&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="問題背景">問題背景&lt;/h2>
&lt;h3 id="現有設計">現有設計&lt;/h3>
&lt;p>&lt;code>hook_validator.py&lt;/code> 使用 Python 的 re 模組進行多種模式匹配驗證：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Optional&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pathlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Path&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">HookValidator&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Hook 合規性驗證器&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="c1"># Pattern definitions for various validation checks&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="n">HOOK_IO_PATTERNS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+hook_io\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+lib\.hook_io\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="n">HOOK_LOGGING_PATTERNS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+hook_logging\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+lib\.hook_logging\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="n">CONFIG_LOADER_PATTERNS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+config_loader\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+lib\.config_loader\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="n">GIT_UTILS_PATTERNS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+git_utils\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;from\s+lib\.git_utils\s+import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> &lt;span class="n">OUTPUT_PATTERNS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;write_hook_output\s*\(&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;create_pretooluse_output\s*\(&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;create_posttooluse_output\s*\(&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl"> &lt;span class="n">BAD_OUTPUT_PATTERNS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;print\s*\(\s*json\.dumps\s*\(&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;sys\.stdout\.write\s*\(\s*json\.dumps\s*\(&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl"> &lt;span class="n">VALID_NAME_PATTERNS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;^[a-z0-9](/python-advanced/06-rust-extensions/case-studies/rust-regex/[a-z0-9\-_]*[a-z0-9])?\.py$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_has_import&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">patterns&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Check if content matches any of the import patterns&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">any&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl"> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">pattern&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">patterns&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_matches_pattern&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">patterns&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Check if content matches any pattern&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">any&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl"> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">pattern&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">patterns&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">check_naming_convention&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">hook_path&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Path&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Validate file naming convention&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl"> &lt;span class="n">filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">hook_path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl"> &lt;span class="n">valid_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">any&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl"> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="k">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">filename&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">pattern&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">VALID_NAME_PATTERNS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">65&lt;/span>&lt;span class="cl"> &lt;span class="c1"># ... validation logic&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這段程式碼展示了幾個核心問題：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>重複編譯&lt;/strong>：每次呼叫 &lt;code>re.search()&lt;/code> 或 &lt;code>re.match()&lt;/code> 都可能重新編譯正則表達式&lt;/li>
&lt;li>&lt;strong>多模式匹配&lt;/strong>：需要遍歷多個模式逐一檢查&lt;/li>
&lt;li>&lt;strong>混合使用場景&lt;/strong>：部分用於 &lt;code>match&lt;/code>（從頭匹配），部分用於 &lt;code>search&lt;/code>（任意位置）&lt;/li>
&lt;/ol>
&lt;h3 id="效能限制">效能限制&lt;/h3>
&lt;p>Python re 模組的限制：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>限制&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;th>影響&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>回溯型引擎&lt;/strong>&lt;/td>
 &lt;td>NFA with backtracking&lt;/td>
 &lt;td>某些模式可能導致指數級時間複雜度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>解釋器開銷&lt;/strong>&lt;/td>
 &lt;td>每次匹配都經過 Python 呼叫&lt;/td>
 &lt;td>大量匹配時累積顯著延遲&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>無硬體加速&lt;/strong>&lt;/td>
 &lt;td>純軟體實作&lt;/td>
 &lt;td>無法利用 SIMD 等現代 CPU 特性&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>GIL 限制&lt;/strong>&lt;/td>
 &lt;td>受 Global Interpreter Lock 影響&lt;/td>
 &lt;td>多執行緒場景效能受限&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h4 id="病態輸入示例">病態輸入示例&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># Pathological pattern: catastrophic backtracking&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="n">pattern&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;(a+)+b&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="n">text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">25&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;c&amp;#34;&lt;/span> &lt;span class="c1"># No match, triggers backtracking&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="n">start&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Python re: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">elapsed&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.2f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">s&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># May take several seconds!&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="進階解決方案">進階解決方案&lt;/h2>
&lt;h3 id="設計目標">設計目標&lt;/h3>
&lt;ol>
&lt;li>用 Rust regex crate 取代 Python re&lt;/li>
&lt;li>利用 Rust regex 的 DFA 引擎確保線性時間複雜度&lt;/li>
&lt;li>使用 &lt;code>RegexSet&lt;/code> 實現高效批次驗證&lt;/li>
&lt;li>預編譯正則表達式，避免重複編譯開銷&lt;/li>
&lt;/ol>
&lt;h3 id="實作步驟">實作步驟&lt;/h3>
&lt;h4 id="步驟-1建立專案結構">步驟 1：建立專案結構&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># Create new maturin project&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">maturin new hook_validator_rs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nb">cd&lt;/span> hook_validator_rs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># Project structure&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">hook_validator_rs/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── Cargo.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">└── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> └── lib.rs&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>編輯 &lt;code>Cargo.toml&lt;/code>：&lt;/p></description><content:encoded><![CDATA[<p>本案例基於 <code>.claude/lib/hook_validator.py</code> 的實際程式碼，展示如何用 Rust 的 regex crate 加速模式匹配。</p>
<h2 id="先備知識">先備知識</h2>
<ul>
<li><a href="/blog/python-advanced/06-rust-extensions/" data-link-title="模組六：用 Rust 擴展 Python" data-link-desc="學習使用 PyO3 和 Maturin 用 Rust 擴展 Python">模組六：用 Rust 擴展 Python</a></li>
<li><a href="/blog/python-advanced/06-rust-extensions/case-studies/pyo3-parser/" data-link-title="案例：PyO3 文字解析" data-link-desc="用 PyO3 和 Rust 實現高效能的 Markdown 連結解析器">6.1 PyO3 文字解析</a></li>
</ul>
<h2 id="問題背景">問題背景</h2>
<h3 id="現有設計">現有設計</h3>
<p><code>hook_validator.py</code> 使用 Python 的 re 模組進行多種模式匹配驗證：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">class</span> <span class="nc">HookValidator</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Hook 合規性驗證器&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1"># Pattern definitions for various validation checks</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">HOOK_IO_PATTERNS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+hook_io\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+lib\.hook_io\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">HOOK_LOGGING_PATTERNS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+hook_logging\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+lib\.hook_logging\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">CONFIG_LOADER_PATTERNS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+config_loader\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+lib\.config_loader\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="n">GIT_UTILS_PATTERNS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+git_utils\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;from\s+lib\.git_utils\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="n">OUTPUT_PATTERNS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;write_hook_output\s*\(&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;create_pretooluse_output\s*\(&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;create_posttooluse_output\s*\(&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="n">BAD_OUTPUT_PATTERNS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="sa">r</span><span class="s1">&#39;print\s*\(\s*json\.dumps\s*\(&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">        <span class="sa">r</span><span class="s1">&#39;sys\.stdout\.write\s*\(\s*json\.dumps\s*\(&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="n">VALID_NAME_PATTERNS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">        <span class="sa">r</span><span class="s2">&#34;^[a-z0-9](/python-advanced/06-rust-extensions/case-studies/rust-regex/[a-z0-9\-_]*[a-z0-9])?\.py$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl">    <span class="k">def</span> <span class="nf">_has_import</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">patterns</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Check if content matches any of the import patterns&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">        <span class="k">return</span> <span class="nb">any</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">            <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">            <span class="k">for</span> <span class="n">pattern</span> <span class="ow">in</span> <span class="n">patterns</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="k">def</span> <span class="nf">_matches_pattern</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">patterns</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Check if content matches any pattern&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">        <span class="k">return</span> <span class="nb">any</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">            <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">            <span class="k">for</span> <span class="n">pattern</span> <span class="ow">in</span> <span class="n">patterns</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">
</span></span><span class="line"><span class="ln">58</span><span class="cl">    <span class="k">def</span> <span class="nf">check_naming_convention</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hook_path</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">dict</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Validate file naming convention&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">        <span class="n">filename</span> <span class="o">=</span> <span class="n">hook_path</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">        <span class="n">valid_name</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">            <span class="n">re</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">filename</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">            <span class="k">for</span> <span class="n">pattern</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">VALID_NAME_PATTERNS</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">        <span class="c1"># ... validation logic</span></span></span></code></pre></div><p>這段程式碼展示了幾個核心問題：</p>
<ol>
<li><strong>重複編譯</strong>：每次呼叫 <code>re.search()</code> 或 <code>re.match()</code> 都可能重新編譯正則表達式</li>
<li><strong>多模式匹配</strong>：需要遍歷多個模式逐一檢查</li>
<li><strong>混合使用場景</strong>：部分用於 <code>match</code>（從頭匹配），部分用於 <code>search</code>（任意位置）</li>
</ol>
<h3 id="效能限制">效能限制</h3>
<p>Python re 模組的限制：</p>
<table>
  <thead>
      <tr>
          <th>限制</th>
          <th>說明</th>
          <th>影響</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>回溯型引擎</strong></td>
          <td>NFA with backtracking</td>
          <td>某些模式可能導致指數級時間複雜度</td>
      </tr>
      <tr>
          <td><strong>解釋器開銷</strong></td>
          <td>每次匹配都經過 Python 呼叫</td>
          <td>大量匹配時累積顯著延遲</td>
      </tr>
      <tr>
          <td><strong>無硬體加速</strong></td>
          <td>純軟體實作</td>
          <td>無法利用 SIMD 等現代 CPU 特性</td>
      </tr>
      <tr>
          <td><strong>GIL 限制</strong></td>
          <td>受 Global Interpreter Lock 影響</td>
          <td>多執行緒場景效能受限</td>
      </tr>
  </tbody>
</table>
<h4 id="病態輸入示例">病態輸入示例</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># Pathological pattern: catastrophic backtracking</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">pattern</span> <span class="o">=</span> <span class="sa">r</span><span class="s2">&#34;(a+)+b&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">text</span> <span class="o">=</span> <span class="s2">&#34;a&#34;</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="s2">&#34;c&#34;</span>  <span class="c1"># No match, triggers backtracking</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Python re: </span><span class="si">{</span><span class="n">elapsed</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2">s&#34;</span><span class="p">)</span>  <span class="c1"># May take several seconds!</span></span></span></code></pre></div><h2 id="進階解決方案">進階解決方案</h2>
<h3 id="設計目標">設計目標</h3>
<ol>
<li>用 Rust regex crate 取代 Python re</li>
<li>利用 Rust regex 的 DFA 引擎確保線性時間複雜度</li>
<li>使用 <code>RegexSet</code> 實現高效批次驗證</li>
<li>預編譯正則表達式，避免重複編譯開銷</li>
</ol>
<h3 id="實作步驟">實作步驟</h3>
<h4 id="步驟-1建立專案結構">步驟 1：建立專案結構</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Create new maturin project</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin new hook_validator_rs
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nb">cd</span> hook_validator_rs
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># Project structure</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">hook_validator_rs/
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── Cargo.toml
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">└── src/
</span></span><span class="line"><span class="ln">10</span><span class="cl">    └── lib.rs</span></span></code></pre></div><p>編輯 <code>Cargo.toml</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;hook_validator_rs&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2021&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;hook_validator_rs&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">pyo3</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.22&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;extension-module&#34;</span><span class="p">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">regex</span> <span class="p">=</span> <span class="s2">&#34;1.10&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">once_cell</span> <span class="p">=</span> <span class="s2">&#34;1.19&#34;</span></span></span></code></pre></div><h4 id="步驟-2定義預編譯正則表達式">步驟 2：定義預編譯正則表達式</h4>
<p>使用 <code>once_cell::sync::Lazy</code> 實現執行緒安全的延遲初始化：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">once_cell</span>::<span class="n">sync</span>::<span class="n">Lazy</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">regex</span>::<span class="p">{</span><span class="n">Regex</span><span class="p">,</span><span class="w"> </span><span class="n">RegexSet</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="c1">// Pre-compiled individual patterns
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="no">HOOK_IO_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+hook_io\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.hook_io\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="p">]).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid regex pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">HOOK_LOGGING_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+hook_logging\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.hook_logging\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="p">]).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid regex pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">CONFIG_LOADER_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+config_loader\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.config_loader\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="p">]).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid regex pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">GIT_UTILS_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+git_utils\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.git_utils\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">    </span><span class="p">]).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid regex pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">OUTPUT_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;write_hook_output\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;create_pretooluse_output\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;create_posttooluse_output\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">    </span><span class="p">]).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid regex pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">BAD_OUTPUT_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;print\s*\(\s*json\.dumps\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;sys\.stdout\.write\s*\(\s*json\.dumps\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">    </span><span class="p">]).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid regex pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w"></span><span class="c1">// For filename validation (anchored match)
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="c1"></span><span class="k">static</span><span class="w"> </span><span class="no">VALID_NAME_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;^[a-z0-9](/python-advanced/06-rust-extensions/case-studies/rust-regex/[a-z0-9\-_]*[a-z0-9])?\.py$&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid regex pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w"></span><span class="p">});</span></span></span></code></pre></div><h5 id="為什麼用-once_cellsynclazy">為什麼用 <code>once_cell::sync::Lazy</code>？</h5>
<ul>
<li><strong>執行緒安全</strong>：<code>Lazy</code> 確保初始化只執行一次，即使多執行緒同時存取</li>
<li><strong>延遲初始化</strong>：只在第一次使用時編譯正則表達式</li>
<li><strong>零執行時開銷</strong>：初始化後的存取是零成本的</li>
</ul>
<h4 id="步驟-3實作批次匹配邏輯">步驟 3：實作批次匹配邏輯</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">collections</span>::<span class="n">HashMap</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="sd">/// Result of validating import patterns in source code
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="sd"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Clone)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_hook_io</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_hook_logging</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_config_loader</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_git_utils</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_good_output</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_bad_output</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__repr__</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="fm">format!</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">            </span><span class="s">&#34;ImportCheckResult(hook_io=</span><span class="si">{}</span><span class="s">, logging=</span><span class="si">{}</span><span class="s">, config=</span><span class="si">{}</span><span class="s">, git=</span><span class="si">{}</span><span class="s">, good_out=</span><span class="si">{}</span><span class="s">, bad_out=</span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_hook_io</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">has_hook_logging</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_config_loader</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">has_git_utils</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_good_output</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">has_bad_output</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w"></span><span class="sd">/// Check all import patterns in a single pass through the content
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">check_imports</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">    </span><span class="n">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">        </span><span class="n">has_hook_io</span>: <span class="nc">HOOK_IO_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">        </span><span class="n">has_hook_logging</span>: <span class="nc">HOOK_LOGGING_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">        </span><span class="n">has_config_loader</span>: <span class="nc">CONFIG_LOADER_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">        </span><span class="n">has_git_utils</span>: <span class="nc">GIT_UTILS_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">        </span><span class="n">has_good_output</span>: <span class="nc">OUTPUT_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">        </span><span class="n">has_bad_output</span>: <span class="nc">BAD_OUTPUT_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w"></span><span class="sd">/// Validate filename against naming convention
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">is_valid_hook_name</span><span class="p">(</span><span class="n">filename</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">    </span><span class="no">VALID_NAME_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w"></span><span class="sd">/// Check which specific patterns matched (for detailed reporting)
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get_matched_patterns</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">pattern_group</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">usize</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">regex_set</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">pattern_group</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">        </span><span class="s">&#34;hook_io&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;*</span><span class="no">HOOK_IO_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">        </span><span class="s">&#34;hook_logging&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;*</span><span class="no">HOOK_LOGGING_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w">        </span><span class="s">&#34;config_loader&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;*</span><span class="no">CONFIG_LOADER_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">        </span><span class="s">&#34;git_utils&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;*</span><span class="no">GIT_UTILS_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="w">        </span><span class="s">&#34;output&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;*</span><span class="no">OUTPUT_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w">        </span><span class="s">&#34;bad_output&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;*</span><span class="no">BAD_OUTPUT_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">        </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[],</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">    </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="w">    </span><span class="n">regex_set</span><span class="p">.</span><span class="n">matches</span><span class="p">(</span><span class="n">content</span><span class="p">).</span><span class="n">iter</span><span class="p">().</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h4 id="步驟-4進階批次驗證-api">步驟 4：進階批次驗證 API</h4>
<p>對於需要一次驗證大量檔案的場景，提供更高效的批次 API：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="sd">/// Batch validation result for multiple files
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="sd"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Clone)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">results</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">ImportCheckResult</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">valid_names</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="kt">bool</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__repr__</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;BatchValidationResult(</span><span class="si">{}</span><span class="s"> files)&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">results</span><span class="p">.</span><span class="n">len</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="sd">/// Get files that are missing hook_io import
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">files_missing_hook_io</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">results</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="o">!</span><span class="n">r</span><span class="p">.</span><span class="n">has_hook_io</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">    </span><span class="sd">/// Get files with bad output patterns
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">files_with_bad_output</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">results</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">has_bad_output</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w"></span><span class="sd">/// Validate multiple files in batch
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="sd">/// This is more efficient than calling check_imports for each file
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="sd">/// because it can potentially parallelize the work.
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">validate_batch</span><span class="p">(</span><span class="n">files</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">results</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">ImportCheckResult</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">files</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">content</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">(),</span><span class="w"> </span><span class="n">check_imports</span><span class="p">(</span><span class="n">content</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">valid_names</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="kt">bool</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">files</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">keys</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="n">path</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">filename</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">path</span><span class="p">.</span><span class="n">rsplit</span><span class="p">(</span><span class="sc">&#39;/&#39;</span><span class="p">).</span><span class="n">next</span><span class="p">().</span><span class="n">unwrap_or</span><span class="p">(</span><span class="n">path</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">            </span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">(),</span><span class="w"> </span><span class="n">is_valid_hook_name</span><span class="p">(</span><span class="n">filename</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">    </span><span class="n">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">results</span><span class="p">,</span><span class="w"> </span><span class="n">valid_names</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h4 id="步驟-5pyo3-模組導出">步驟 5：PyO3 模組導出</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="sd">/// Rust-powered hook validator with pre-compiled regex patterns
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="sd"></span><span class="cp">#[pymodule]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">hook_validator_rs</span><span class="p">(</span><span class="n">m</span>: <span class="kp">&amp;</span><span class="nc">Bound</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="p">,</span><span class="w"> </span><span class="n">PyModule</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">check_imports</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">is_valid_hook_name</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">get_matched_patterns</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">validate_batch</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_class</span>::<span class="o">&lt;</span><span class="n">ImportCheckResult</span><span class="o">&gt;</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_class</span>::<span class="o">&lt;</span><span class="n">BatchValidationResult</span><span class="o">&gt;</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nb">Ok</span><span class="p">(())</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h4 id="步驟-6python-端整合">步驟 6：Python 端整合</h4>
<p>在 Python 端無縫整合 Rust 模組：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">  1</span><span class="cl"><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="s2">Hook 合規性驗證工具（Rust 加速版）
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="s2">This module provides a drop-in replacement for the pure Python
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="s2">hook_validator, using Rust regex crate for pattern matching.
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl">
</span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span><span class="p">,</span> <span class="n">field</span>
</span></span><span class="line"><span class="ln"> 11</span><span class="cl">
</span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="c1"># Try to import Rust extension, fall back to pure Python</span>
</span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl">    <span class="kn">import</span> <span class="nn">hook_validator_rs</span> <span class="k">as</span> <span class="nn">_rs</span>
</span></span><span class="line"><span class="ln"> 15</span><span class="cl">    <span class="n">_USE_RUST</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 17</span><span class="cl">    <span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">    <span class="n">_USE_RUST</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln"> 19</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Warning: Rust extension not available, using pure Python&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 20</span><span class="cl">
</span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="nd">@dataclass</span>
</span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="k">class</span> <span class="nc">ValidationIssue</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Validation issue description&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">    <span class="n">level</span><span class="p">:</span> <span class="nb">str</span>  <span class="c1"># &#34;error&#34; | &#34;warning&#34; | &#34;info&#34;</span>
</span></span><span class="line"><span class="ln"> 25</span><span class="cl">    <span class="n">message</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="ln"> 26</span><span class="cl">    <span class="n">line</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="ln"> 27</span><span class="cl">    <span class="n">suggestion</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="ln"> 28</span><span class="cl">
</span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="nd">@dataclass</span>
</span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="k">class</span> <span class="nc">ValidationResult</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 31</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Validation result for a single hook&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 32</span><span class="cl">    <span class="n">hook_path</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl">    <span class="n">issues</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">ValidationIssue</span><span class="p">]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">default_factory</span><span class="o">=</span><span class="nb">list</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 34</span><span class="cl">    <span class="n">is_compliant</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="ln"> 35</span><span class="cl">
</span></span><span class="line"><span class="ln"> 36</span><span class="cl">    <span class="k">def</span> <span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 37</span><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">is_compliant</span> <span class="o">=</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 38</span><span class="cl">            <span class="n">issue</span><span class="o">.</span><span class="n">level</span> <span class="o">==</span> <span class="s2">&#34;error&#34;</span> <span class="k">for</span> <span class="n">issue</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">issues</span>
</span></span><span class="line"><span class="ln"> 39</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln"> 40</span><span class="cl">
</span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="k">class</span> <span class="nc">HookValidator</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Hook compliance validator with optional Rust acceleration&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 43</span><span class="cl">
</span></span><span class="line"><span class="ln"> 44</span><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">project_root</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 45</span><span class="cl">        <span class="k">if</span> <span class="n">project_root</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">            <span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="ln"> 47</span><span class="cl">            <span class="n">project_root</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;CLAUDE_PROJECT_DIR&#34;</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">project_root</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">project_root</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl">
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">    <span class="k">def</span> <span class="nf">check_lib_imports</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 51</span><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 52</span><span class="cl">        <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 53</span><span class="cl">        <span class="n">hook_path</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Path</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="ln"> 54</span><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">ValidationIssue</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 55</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Check shared module imports using Rust regex&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 56</span><span class="cl">        <span class="n">issues</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 57</span><span class="cl">
</span></span><span class="line"><span class="ln"> 58</span><span class="cl">        <span class="k">if</span> <span class="n">_USE_RUST</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl">            <span class="c1"># Use Rust-accelerated pattern matching</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">            <span class="n">result</span> <span class="o">=</span> <span class="n">_rs</span><span class="o">.</span><span class="n">check_imports</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 61</span><span class="cl">
</span></span><span class="line"><span class="ln"> 62</span><span class="cl">            <span class="k">if</span> <span class="ow">not</span> <span class="n">result</span><span class="o">.</span><span class="n">has_hook_io</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">                <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 64</span><span class="cl">                    <span class="n">level</span><span class="o">=</span><span class="s2">&#34;warning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl">                    <span class="n">message</span><span class="o">=</span><span class="s2">&#34;Missing hook_io import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">                    <span class="n">suggestion</span><span class="o">=</span><span class="s2">&#34;Add: from hook_io import read_hook_input, write_hook_output&#34;</span>
</span></span><span class="line"><span class="ln"> 67</span><span class="cl">                <span class="p">))</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">
</span></span><span class="line"><span class="ln"> 69</span><span class="cl">            <span class="k">if</span> <span class="ow">not</span> <span class="n">result</span><span class="o">.</span><span class="n">has_hook_logging</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">                <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 71</span><span class="cl">                    <span class="n">level</span><span class="o">=</span><span class="s2">&#34;info&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl">                    <span class="n">message</span><span class="o">=</span><span class="s2">&#34;Missing hook_logging import (recommended)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">                    <span class="n">suggestion</span><span class="o">=</span><span class="s2">&#34;Add: from hook_logging import setup_hook_logging&#34;</span>
</span></span><span class="line"><span class="ln"> 74</span><span class="cl">                <span class="p">))</span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl">
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">            <span class="k">if</span> <span class="n">result</span><span class="o">.</span><span class="n">has_bad_output</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">                <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">                    <span class="n">level</span><span class="o">=</span><span class="s2">&#34;warning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">                    <span class="n">message</span><span class="o">=</span><span class="s2">&#34;Using print(json.dumps(...)) instead of write_hook_output()&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">                    <span class="n">suggestion</span><span class="o">=</span><span class="s2">&#34;Replace with: write_hook_output(output_dict)&#34;</span>
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">                <span class="p">))</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">            <span class="c1"># Fallback to pure Python regex</span>
</span></span><span class="line"><span class="ln"> 84</span><span class="cl">            <span class="n">issues</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_check_imports_python</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">hook_path</span><span class="p">))</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">
</span></span><span class="line"><span class="ln"> 86</span><span class="cl">        <span class="k">return</span> <span class="n">issues</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl">
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">    <span class="k">def</span> <span class="nf">check_naming_convention</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hook_path</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">ValidationIssue</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Validate filename against naming convention&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">        <span class="n">issues</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">        <span class="n">filename</span> <span class="o">=</span> <span class="n">hook_path</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">
</span></span><span class="line"><span class="ln"> 93</span><span class="cl">        <span class="k">if</span> <span class="n">_USE_RUST</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">            <span class="n">valid</span> <span class="o">=</span> <span class="n">_rs</span><span class="o">.</span><span class="n">is_valid_hook_name</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 96</span><span class="cl">            <span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">            <span class="n">valid</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">re</span><span class="o">.</span><span class="k">match</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">                <span class="sa">r</span><span class="s2">&#34;^[a-z0-9](/python-advanced/06-rust-extensions/case-studies/rust-regex/[a-z0-9\-_]*[a-z0-9])?\.py$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">                <span class="n">filename</span>
</span></span><span class="line"><span class="ln">100</span><span class="cl">            <span class="p">))</span>
</span></span><span class="line"><span class="ln">101</span><span class="cl">
</span></span><span class="line"><span class="ln">102</span><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">valid</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">            <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl">                <span class="n">level</span><span class="o">=</span><span class="s2">&#34;warning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">105</span><span class="cl">                <span class="n">message</span><span class="o">=</span><span class="sa">f</span><span class="s2">&#34;Invalid filename: </span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">106</span><span class="cl">                <span class="n">suggestion</span><span class="o">=</span><span class="s2">&#34;Use snake-case or kebab-case: check_permissions.py&#34;</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl">            <span class="p">))</span>
</span></span><span class="line"><span class="ln">108</span><span class="cl">
</span></span><span class="line"><span class="ln">109</span><span class="cl">        <span class="k">return</span> <span class="n">issues</span>
</span></span><span class="line"><span class="ln">110</span><span class="cl">
</span></span><span class="line"><span class="ln">111</span><span class="cl">    <span class="k">def</span> <span class="nf">validate_hook</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hook_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ValidationResult</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">112</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Validate a single hook file&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">113</span><span class="cl">        <span class="n">path</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_resolve_path</span><span class="p">(</span><span class="n">hook_path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">114</span><span class="cl">
</span></span><span class="line"><span class="ln">115</span><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">            <span class="k">return</span> <span class="n">ValidationResult</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">117</span><span class="cl">                <span class="n">hook_path</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">118</span><span class="cl">                <span class="n">issues</span><span class="o">=</span><span class="p">[</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">119</span><span class="cl">                    <span class="n">level</span><span class="o">=</span><span class="s2">&#34;error&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">120</span><span class="cl">                    <span class="n">message</span><span class="o">=</span><span class="sa">f</span><span class="s2">&#34;Hook file not found: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">121</span><span class="cl">                <span class="p">)]</span>
</span></span><span class="line"><span class="ln">122</span><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="ln">123</span><span class="cl">
</span></span><span class="line"><span class="ln">124</span><span class="cl">        <span class="n">content</span> <span class="o">=</span> <span class="n">path</span><span class="o">.</span><span class="n">read_text</span><span class="p">(</span><span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">125</span><span class="cl">        <span class="n">issues</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">126</span><span class="cl">        <span class="n">issues</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">check_naming_convention</span><span class="p">(</span><span class="n">path</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">127</span><span class="cl">        <span class="n">issues</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">check_lib_imports</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">path</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">128</span><span class="cl">
</span></span><span class="line"><span class="ln">129</span><span class="cl">        <span class="k">return</span> <span class="n">ValidationResult</span><span class="p">(</span><span class="n">hook_path</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">),</span> <span class="n">issues</span><span class="o">=</span><span class="n">issues</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">130</span><span class="cl">
</span></span><span class="line"><span class="ln">131</span><span class="cl">    <span class="k">def</span> <span class="nf">validate_all_hooks</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">132</span><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">133</span><span class="cl">        <span class="n">hooks_dir</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="ln">134</span><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">ValidationResult</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">135</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Validate all hooks with batch optimization&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">136</span><span class="cl">        <span class="k">if</span> <span class="n">hooks_dir</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">137</span><span class="cl">            <span class="n">hooks_dir</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">project_root</span> <span class="o">/</span> <span class="s2">&#34;.claude&#34;</span> <span class="o">/</span> <span class="s2">&#34;hooks&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">138</span><span class="cl">
</span></span><span class="line"><span class="ln">139</span><span class="cl">        <span class="n">hooks_path</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_resolve_path</span><span class="p">(</span><span class="n">hooks_dir</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">140</span><span class="cl">        <span class="n">hook_files</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">hooks_path</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s2">&#34;*.py&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">141</span><span class="cl">
</span></span><span class="line"><span class="ln">142</span><span class="cl">        <span class="k">if</span> <span class="n">_USE_RUST</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">hook_files</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">143</span><span class="cl">            <span class="c1"># Use batch validation for multiple files</span>
</span></span><span class="line"><span class="ln">144</span><span class="cl">            <span class="n">files_content</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">145</span><span class="cl">                <span class="nb">str</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> <span class="n">f</span><span class="o">.</span><span class="n">read_text</span><span class="p">(</span><span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">146</span><span class="cl">                <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">hook_files</span>
</span></span><span class="line"><span class="ln">147</span><span class="cl">                <span class="k">if</span> <span class="ow">not</span> <span class="n">f</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;_&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">148</span><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="ln">149</span><span class="cl">
</span></span><span class="line"><span class="ln">150</span><span class="cl">            <span class="n">batch_result</span> <span class="o">=</span> <span class="n">_rs</span><span class="o">.</span><span class="n">validate_batch</span><span class="p">(</span><span class="n">files_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">151</span><span class="cl">
</span></span><span class="line"><span class="ln">152</span><span class="cl">            <span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">153</span><span class="cl">            <span class="k">for</span> <span class="n">path</span><span class="p">,</span> <span class="n">content</span> <span class="ow">in</span> <span class="n">files_content</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">154</span><span class="cl">                <span class="n">import_result</span> <span class="o">=</span> <span class="n">batch_result</span><span class="o">.</span><span class="n">results</span><span class="p">[</span><span class="n">path</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">155</span><span class="cl">                <span class="n">valid_name</span> <span class="o">=</span> <span class="n">batch_result</span><span class="o">.</span><span class="n">valid_names</span><span class="p">[</span><span class="n">path</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">156</span><span class="cl">
</span></span><span class="line"><span class="ln">157</span><span class="cl">                <span class="n">issues</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_import_result_to_issues</span><span class="p">(</span><span class="n">import_result</span><span class="p">,</span> <span class="n">valid_name</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">158</span><span class="cl">                <span class="n">results</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationResult</span><span class="p">(</span><span class="n">hook_path</span><span class="o">=</span><span class="n">path</span><span class="p">,</span> <span class="n">issues</span><span class="o">=</span><span class="n">issues</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">159</span><span class="cl">
</span></span><span class="line"><span class="ln">160</span><span class="cl">            <span class="k">return</span> <span class="n">results</span>
</span></span><span class="line"><span class="ln">161</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">162</span><span class="cl">            <span class="c1"># Single file or no Rust: use standard validation</span>
</span></span><span class="line"><span class="ln">163</span><span class="cl">            <span class="k">return</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">164</span><span class="cl">                <span class="bp">self</span><span class="o">.</span><span class="n">validate_hook</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">f</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">165</span><span class="cl">                <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">hook_files</span>
</span></span><span class="line"><span class="ln">166</span><span class="cl">                <span class="k">if</span> <span class="ow">not</span> <span class="n">f</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;_&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">167</span><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="ln">168</span><span class="cl">
</span></span><span class="line"><span class="ln">169</span><span class="cl">    <span class="k">def</span> <span class="nf">_resolve_path</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Path</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">170</span><span class="cl">        <span class="n">p</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">171</span><span class="cl">        <span class="k">return</span> <span class="n">p</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">is_absolute</span><span class="p">()</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">project_root</span> <span class="o">/</span> <span class="n">p</span>
</span></span><span class="line"><span class="ln">172</span><span class="cl">
</span></span><span class="line"><span class="ln">173</span><span class="cl">    <span class="k">def</span> <span class="nf">_import_result_to_issues</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">174</span><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">175</span><span class="cl">        <span class="n">result</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">176</span><span class="cl">        <span class="n">valid_name</span><span class="p">:</span> <span class="nb">bool</span>
</span></span><span class="line"><span class="ln">177</span><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">ValidationIssue</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">178</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Convert Rust ImportCheckResult to list of issues&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">179</span><span class="cl">        <span class="n">issues</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">180</span><span class="cl">
</span></span><span class="line"><span class="ln">181</span><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">valid_name</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">182</span><span class="cl">            <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">183</span><span class="cl">                <span class="n">level</span><span class="o">=</span><span class="s2">&#34;warning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">184</span><span class="cl">                <span class="n">message</span><span class="o">=</span><span class="s2">&#34;Invalid filename format&#34;</span>
</span></span><span class="line"><span class="ln">185</span><span class="cl">            <span class="p">))</span>
</span></span><span class="line"><span class="ln">186</span><span class="cl">
</span></span><span class="line"><span class="ln">187</span><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">result</span><span class="o">.</span><span class="n">has_hook_io</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">188</span><span class="cl">            <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">189</span><span class="cl">                <span class="n">level</span><span class="o">=</span><span class="s2">&#34;warning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">190</span><span class="cl">                <span class="n">message</span><span class="o">=</span><span class="s2">&#34;Missing hook_io import&#34;</span>
</span></span><span class="line"><span class="ln">191</span><span class="cl">            <span class="p">))</span>
</span></span><span class="line"><span class="ln">192</span><span class="cl">
</span></span><span class="line"><span class="ln">193</span><span class="cl">        <span class="k">if</span> <span class="n">result</span><span class="o">.</span><span class="n">has_bad_output</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">194</span><span class="cl">            <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">195</span><span class="cl">                <span class="n">level</span><span class="o">=</span><span class="s2">&#34;warning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">196</span><span class="cl">                <span class="n">message</span><span class="o">=</span><span class="s2">&#34;Using deprecated output pattern&#34;</span>
</span></span><span class="line"><span class="ln">197</span><span class="cl">            <span class="p">))</span>
</span></span><span class="line"><span class="ln">198</span><span class="cl">
</span></span><span class="line"><span class="ln">199</span><span class="cl">        <span class="k">return</span> <span class="n">issues</span>
</span></span><span class="line"><span class="ln">200</span><span class="cl">
</span></span><span class="line"><span class="ln">201</span><span class="cl">    <span class="k">def</span> <span class="nf">_check_imports_python</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">202</span><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">203</span><span class="cl">        <span class="n">content</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">204</span><span class="cl">        <span class="n">hook_path</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Path</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">205</span><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">ValidationIssue</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">206</span><span class="cl">        <span class="s2">&#34;&#34;&#34;Pure Python fallback for import checking&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">207</span><span class="cl">        <span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln">208</span><span class="cl">        <span class="n">issues</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">209</span><span class="cl">
</span></span><span class="line"><span class="ln">210</span><span class="cl">        <span class="n">hook_io_patterns</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">211</span><span class="cl">            <span class="sa">r</span><span class="s2">&#34;from\s+hook_io\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">212</span><span class="cl">            <span class="sa">r</span><span class="s2">&#34;from\s+lib\.hook_io\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">213</span><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="ln">214</span><span class="cl">
</span></span><span class="line"><span class="ln">215</span><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span><span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">hook_io_patterns</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">216</span><span class="cl">            <span class="n">issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ValidationIssue</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">217</span><span class="cl">                <span class="n">level</span><span class="o">=</span><span class="s2">&#34;warning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">218</span><span class="cl">                <span class="n">message</span><span class="o">=</span><span class="s2">&#34;Missing hook_io import&#34;</span>
</span></span><span class="line"><span class="ln">219</span><span class="cl">            <span class="p">))</span>
</span></span><span class="line"><span class="ln">220</span><span class="cl">
</span></span><span class="line"><span class="ln">221</span><span class="cl">        <span class="k">return</span> <span class="n">issues</span></span></span></code></pre></div><h3 id="完整程式碼">完整程式碼</h3>
<p>以下是完整的 <code>src/lib.rs</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">  1</span><span class="cl"><span class="sd">//! Hook Validator - Rust regex acceleration for Python hook validation
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="sd">//!
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="sd">//! This module provides pre-compiled regex patterns for validating
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="sd">//! Claude Code hook files, with significant performance improvements
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="sd">//! over pure Python regex.
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="sd"></span><span class="w">
</span></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">once_cell</span>::<span class="n">sync</span>::<span class="n">Lazy</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">regex</span>::<span class="p">{</span><span class="n">Regex</span><span class="p">,</span><span class="w"> </span><span class="n">RegexSet</span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">collections</span>::<span class="n">HashMap</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="c1">// Pre-compiled Regex Patterns
</span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="w"></span><span class="sd">/// Import patterns for hook_io module
</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">HOOK_IO_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+hook_io\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.hook_io\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="w">    </span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid HOOK_IO_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="w"></span><span class="sd">/// Import patterns for hook_logging module
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">HOOK_LOGGING_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+hook_logging\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.hook_logging\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="w">    </span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid HOOK_LOGGING_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="w"></span><span class="sd">/// Import patterns for config_loader module
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">CONFIG_LOADER_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+config_loader\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.config_loader\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="w">    </span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid CONFIG_LOADER_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="w"></span><span class="sd">/// Import patterns for git_utils module
</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">GIT_UTILS_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+git_utils\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;from\s+lib\.git_utils\s+import&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="w">    </span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid GIT_UTILS_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="w"></span><span class="sd">/// Recommended output function patterns
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">OUTPUT_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;write_hook_output\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;create_pretooluse_output\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;create_posttooluse_output\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="w">    </span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid OUTPUT_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="w"></span><span class="sd">/// Deprecated output patterns (should be avoided)
</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">BAD_OUTPUT_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;print\s*\(\s*json\.dumps\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;sys\.stdout\.write\s*\(\s*json\.dumps\s*\(&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="w">    </span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid BAD_OUTPUT_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="w"></span><span class="sd">/// Valid hook filename pattern (anchored)
</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">VALID_NAME_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;^[a-z0-9](/python-advanced/06-rust-extensions/case-studies/rust-regex/[a-z0-9\-_]*[a-z0-9])?\.py$&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid VALID_NAME_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="w"></span><span class="sd">/// JSON output detection patterns
</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="sd"></span><span class="k">static</span><span class="w"> </span><span class="no">JSON_OUTPUT_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;json\.dumps&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;write_hook_output&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="w">        </span><span class="sa">r</span><span class="s">&#34;create_.*_output&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="w">    </span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid JSON_OUTPUT_REGEX pattern&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="c1">// Result Types
</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="w"></span><span class="sd">/// Result of checking import patterns in source code
</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="sd"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Clone, Debug)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_hook_io</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_hook_logging</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_config_loader</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_git_utils</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_good_output</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_bad_output</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">108</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">has_json_output</span>: <span class="kt">bool</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">111</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln">112</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">113</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__repr__</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">114</span><span class="cl"><span class="w">        </span><span class="fm">format!</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">115</span><span class="cl"><span class="w">            </span><span class="s">&#34;ImportCheckResult(hook_io=</span><span class="si">{}</span><span class="s">, logging=</span><span class="si">{}</span><span class="s">, config=</span><span class="si">{}</span><span class="s">, git=</span><span class="si">{}</span><span class="s">, </span><span class="se">\</span><span class="s">
</span></span></span><span class="line"><span class="ln">116</span><span class="cl"><span class="s">             good_out=</span><span class="si">{}</span><span class="s">, bad_out=</span><span class="si">{}</span><span class="s">, json_out=</span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">117</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_hook_io</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">118</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_hook_logging</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">119</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_config_loader</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">120</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_git_utils</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">121</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_good_output</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">122</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_bad_output</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">123</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">has_json_output</span><span class="w">
</span></span></span><span class="line"><span class="ln">124</span><span class="cl"><span class="w">        </span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">125</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">126</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">127</span><span class="cl"><span class="w">    </span><span class="sd">/// Check if the hook uses recommended output patterns
</span></span></span><span class="line"><span class="ln">128</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">uses_recommended_output</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">129</span><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">has_good_output</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="o">!</span><span class="bp">self</span><span class="p">.</span><span class="n">has_bad_output</span><span class="w">
</span></span></span><span class="line"><span class="ln">130</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">131</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">132</span><span class="cl"><span class="w">    </span><span class="sd">/// Check if the hook has all required imports
</span></span></span><span class="line"><span class="ln">133</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">has_required_imports</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">134</span><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">has_hook_io</span><span class="w">
</span></span></span><span class="line"><span class="ln">135</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">136</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">137</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">138</span><span class="cl"><span class="w"></span><span class="sd">/// Batch validation result for multiple files
</span></span></span><span class="line"><span class="ln">139</span><span class="cl"><span class="sd"></span><span class="cp">#[pyclass]</span><span class="w">
</span></span></span><span class="line"><span class="ln">140</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Clone, Debug)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">141</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">142</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">143</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">results</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">ImportCheckResult</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">144</span><span class="cl"><span class="w">    </span><span class="cp">#[pyo3(get)]</span><span class="w">
</span></span></span><span class="line"><span class="ln">145</span><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">valid_names</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="kt">bool</span><span class="o">&gt;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">146</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">147</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">148</span><span class="cl"><span class="w"></span><span class="cp">#[pymethods]</span><span class="w">
</span></span></span><span class="line"><span class="ln">149</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">150</span><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">__repr__</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">151</span><span class="cl"><span class="w">        </span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;BatchValidationResult(</span><span class="si">{}</span><span class="s"> files)&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">results</span><span class="p">.</span><span class="n">len</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">152</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">153</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">154</span><span class="cl"><span class="w">    </span><span class="sd">/// Get list of files missing hook_io import
</span></span></span><span class="line"><span class="ln">155</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">files_missing_hook_io</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">156</span><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">results</span><span class="w">
</span></span></span><span class="line"><span class="ln">157</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">158</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="o">!</span><span class="n">r</span><span class="p">.</span><span class="n">has_hook_io</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">159</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">160</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">161</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">162</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">163</span><span class="cl"><span class="w">    </span><span class="sd">/// Get list of files using bad output patterns
</span></span></span><span class="line"><span class="ln">164</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">files_with_bad_output</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">165</span><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">results</span><span class="w">
</span></span></span><span class="line"><span class="ln">166</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">167</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">r</span><span class="p">.</span><span class="n">has_bad_output</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">168</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">169</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">170</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">171</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">172</span><span class="cl"><span class="w">    </span><span class="sd">/// Get list of files with invalid names
</span></span></span><span class="line"><span class="ln">173</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">files_with_invalid_names</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">174</span><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">valid_names</span><span class="w">
</span></span></span><span class="line"><span class="ln">175</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">176</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">valid</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="o">!*</span><span class="n">valid</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">177</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="ln">178</span><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">179</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">180</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">181</span><span class="cl"><span class="w">    </span><span class="sd">/// Get summary statistics
</span></span></span><span class="line"><span class="ln">182</span><span class="cl"><span class="sd"></span><span class="w">    </span><span class="k">fn</span> <span class="nf">summary</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="kt">usize</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">183</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">stats</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">HashMap</span>::<span class="n">new</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">184</span><span class="cl"><span class="w">        </span><span class="n">stats</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="s">&#34;total&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">(),</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">results</span><span class="p">.</span><span class="n">len</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="ln">185</span><span class="cl"><span class="w">        </span><span class="n">stats</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">186</span><span class="cl"><span class="w">            </span><span class="s">&#34;missing_hook_io&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">187</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">files_missing_hook_io</span><span class="p">().</span><span class="n">len</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">188</span><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">189</span><span class="cl"><span class="w">        </span><span class="n">stats</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">190</span><span class="cl"><span class="w">            </span><span class="s">&#34;bad_output&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">191</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">files_with_bad_output</span><span class="p">().</span><span class="n">len</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">192</span><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">193</span><span class="cl"><span class="w">        </span><span class="n">stats</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">194</span><span class="cl"><span class="w">            </span><span class="s">&#34;invalid_names&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">195</span><span class="cl"><span class="w">            </span><span class="bp">self</span><span class="p">.</span><span class="n">files_with_invalid_names</span><span class="p">().</span><span class="n">len</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">196</span><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">197</span><span class="cl"><span class="w">        </span><span class="n">stats</span><span class="w">
</span></span></span><span class="line"><span class="ln">198</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">199</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">200</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">201</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">202</span><span class="cl"><span class="c1">// Public API Functions
</span></span></span><span class="line"><span class="ln">203</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">204</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">205</span><span class="cl"><span class="w"></span><span class="sd">/// Check all import patterns in source code
</span></span></span><span class="line"><span class="ln">206</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">207</span><span class="cl"><span class="sd">/// This function performs all pattern checks in a single pass through
</span></span></span><span class="line"><span class="ln">208</span><span class="cl"><span class="sd">/// the content, making it much more efficient than individual checks.
</span></span></span><span class="line"><span class="ln">209</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">210</span><span class="cl"><span class="sd">/// # Arguments
</span></span></span><span class="line"><span class="ln">211</span><span class="cl"><span class="sd">/// * `content` - The source code content to check
</span></span></span><span class="line"><span class="ln">212</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">213</span><span class="cl"><span class="sd">/// # Returns
</span></span></span><span class="line"><span class="ln">214</span><span class="cl"><span class="sd">/// * `ImportCheckResult` - Results of all pattern checks
</span></span></span><span class="line"><span class="ln">215</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">216</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">check_imports</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">217</span><span class="cl"><span class="w">    </span><span class="n">ImportCheckResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">218</span><span class="cl"><span class="w">        </span><span class="n">has_hook_io</span>: <span class="nc">HOOK_IO_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">219</span><span class="cl"><span class="w">        </span><span class="n">has_hook_logging</span>: <span class="nc">HOOK_LOGGING_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">220</span><span class="cl"><span class="w">        </span><span class="n">has_config_loader</span>: <span class="nc">CONFIG_LOADER_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">221</span><span class="cl"><span class="w">        </span><span class="n">has_git_utils</span>: <span class="nc">GIT_UTILS_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">222</span><span class="cl"><span class="w">        </span><span class="n">has_good_output</span>: <span class="nc">OUTPUT_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">223</span><span class="cl"><span class="w">        </span><span class="n">has_bad_output</span>: <span class="nc">BAD_OUTPUT_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">224</span><span class="cl"><span class="w">        </span><span class="n">has_json_output</span>: <span class="nc">JSON_OUTPUT_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">225</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">226</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">227</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">228</span><span class="cl"><span class="w"></span><span class="sd">/// Validate filename against naming convention
</span></span></span><span class="line"><span class="ln">229</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">230</span><span class="cl"><span class="sd">/// Valid names must:
</span></span></span><span class="line"><span class="ln">231</span><span class="cl"><span class="sd">/// - Start and end with lowercase alphanumeric
</span></span></span><span class="line"><span class="ln">232</span><span class="cl"><span class="sd">/// - Contain only lowercase letters, numbers, hyphens, underscores
</span></span></span><span class="line"><span class="ln">233</span><span class="cl"><span class="sd">/// - Have .py extension
</span></span></span><span class="line"><span class="ln">234</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">235</span><span class="cl"><span class="sd">/// # Arguments
</span></span></span><span class="line"><span class="ln">236</span><span class="cl"><span class="sd">/// * `filename` - The filename to validate (just the name, not full path)
</span></span></span><span class="line"><span class="ln">237</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">238</span><span class="cl"><span class="sd">/// # Returns
</span></span></span><span class="line"><span class="ln">239</span><span class="cl"><span class="sd">/// * `bool` - True if the filename is valid
</span></span></span><span class="line"><span class="ln">240</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">241</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">is_valid_hook_name</span><span class="p">(</span><span class="n">filename</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">242</span><span class="cl"><span class="w">    </span><span class="no">VALID_NAME_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">243</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">244</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">245</span><span class="cl"><span class="w"></span><span class="sd">/// Get indices of matched patterns in a pattern group
</span></span></span><span class="line"><span class="ln">246</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">247</span><span class="cl"><span class="sd">/// Useful for detailed reporting of which specific patterns matched.
</span></span></span><span class="line"><span class="ln">248</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">249</span><span class="cl"><span class="sd">/// # Arguments
</span></span></span><span class="line"><span class="ln">250</span><span class="cl"><span class="sd">/// * `content` - The source code content to check
</span></span></span><span class="line"><span class="ln">251</span><span class="cl"><span class="sd">/// * `pattern_group` - One of: &#34;hook_io&#34;, &#34;hook_logging&#34;, &#34;config_loader&#34;,
</span></span></span><span class="line"><span class="ln">252</span><span class="cl"><span class="sd">///                     &#34;git_utils&#34;, &#34;output&#34;, &#34;bad_output&#34;, &#34;json_output&#34;
</span></span></span><span class="line"><span class="ln">253</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">254</span><span class="cl"><span class="sd">/// # Returns
</span></span></span><span class="line"><span class="ln">255</span><span class="cl"><span class="sd">/// * `Vec&lt;usize&gt;` - Indices of patterns that matched
</span></span></span><span class="line"><span class="ln">256</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">257</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get_matched_patterns</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">pattern_group</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">usize</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">258</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">regex_set</span>: <span class="kp">&amp;</span><span class="nc">RegexSet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">pattern_group</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">259</span><span class="cl"><span class="w">        </span><span class="s">&#34;hook_io&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;</span><span class="no">HOOK_IO_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">260</span><span class="cl"><span class="w">        </span><span class="s">&#34;hook_logging&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;</span><span class="no">HOOK_LOGGING_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">261</span><span class="cl"><span class="w">        </span><span class="s">&#34;config_loader&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;</span><span class="no">CONFIG_LOADER_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">262</span><span class="cl"><span class="w">        </span><span class="s">&#34;git_utils&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;</span><span class="no">GIT_UTILS_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">263</span><span class="cl"><span class="w">        </span><span class="s">&#34;output&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;</span><span class="no">OUTPUT_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">264</span><span class="cl"><span class="w">        </span><span class="s">&#34;bad_output&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;</span><span class="no">BAD_OUTPUT_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">265</span><span class="cl"><span class="w">        </span><span class="s">&#34;json_output&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="o">&amp;</span><span class="no">JSON_OUTPUT_REGEX</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">266</span><span class="cl"><span class="w">        </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[],</span><span class="w">
</span></span></span><span class="line"><span class="ln">267</span><span class="cl"><span class="w">    </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="ln">268</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">269</span><span class="cl"><span class="w">    </span><span class="n">regex_set</span><span class="p">.</span><span class="n">matches</span><span class="p">(</span><span class="n">content</span><span class="p">).</span><span class="n">iter</span><span class="p">().</span><span class="n">collect</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">270</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">271</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">272</span><span class="cl"><span class="w"></span><span class="sd">/// Validate multiple files in a single batch operation
</span></span></span><span class="line"><span class="ln">273</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">274</span><span class="cl"><span class="sd">/// This is significantly more efficient than validating files one by one,
</span></span></span><span class="line"><span class="ln">275</span><span class="cl"><span class="sd">/// especially when dealing with many files.
</span></span></span><span class="line"><span class="ln">276</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">277</span><span class="cl"><span class="sd">/// # Arguments
</span></span></span><span class="line"><span class="ln">278</span><span class="cl"><span class="sd">/// * `files` - HashMap of file paths to their contents
</span></span></span><span class="line"><span class="ln">279</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">280</span><span class="cl"><span class="sd">/// # Returns
</span></span></span><span class="line"><span class="ln">281</span><span class="cl"><span class="sd">/// * `BatchValidationResult` - Combined results for all files
</span></span></span><span class="line"><span class="ln">282</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">283</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">validate_batch</span><span class="p">(</span><span class="n">files</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">284</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">results</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">ImportCheckResult</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">files</span><span class="w">
</span></span></span><span class="line"><span class="ln">285</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">286</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">content</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">(),</span><span class="w"> </span><span class="n">check_imports</span><span class="p">(</span><span class="n">content</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="ln">287</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">288</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">289</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">valid_names</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="kt">bool</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">files</span><span class="w">
</span></span></span><span class="line"><span class="ln">290</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">keys</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">291</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="n">path</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">292</span><span class="cl"><span class="w">            </span><span class="c1">// Extract filename from path
</span></span></span><span class="line"><span class="ln">293</span><span class="cl"><span class="c1"></span><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">filename</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">path</span><span class="p">.</span><span class="n">rsplit</span><span class="p">(</span><span class="sc">&#39;/&#39;</span><span class="p">).</span><span class="n">next</span><span class="p">().</span><span class="n">unwrap_or</span><span class="p">(</span><span class="n">path</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">294</span><span class="cl"><span class="w">            </span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">clone</span><span class="p">(),</span><span class="w"> </span><span class="n">is_valid_hook_name</span><span class="p">(</span><span class="n">filename</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">295</span><span class="cl"><span class="w">        </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">296</span><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">297</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">298</span><span class="cl"><span class="w">    </span><span class="n">BatchValidationResult</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">299</span><span class="cl"><span class="w">        </span><span class="n">results</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">300</span><span class="cl"><span class="w">        </span><span class="n">valid_names</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">301</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">302</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">303</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">304</span><span class="cl"><span class="w"></span><span class="sd">/// Check if content contains specific import pattern (simple check)
</span></span></span><span class="line"><span class="ln">305</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">306</span><span class="cl"><span class="sd">/// # Arguments
</span></span></span><span class="line"><span class="ln">307</span><span class="cl"><span class="sd">/// * `content` - The source code to check
</span></span></span><span class="line"><span class="ln">308</span><span class="cl"><span class="sd">/// * `module_name` - The module to check for: &#34;hook_io&#34;, &#34;hook_logging&#34;, etc.
</span></span></span><span class="line"><span class="ln">309</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">310</span><span class="cl"><span class="sd">/// # Returns
</span></span></span><span class="line"><span class="ln">311</span><span class="cl"><span class="sd">/// * `bool` - True if the import pattern is found
</span></span></span><span class="line"><span class="ln">312</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">313</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">has_import</span><span class="p">(</span><span class="n">content</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">module_name</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">314</span><span class="cl"><span class="w">    </span><span class="k">match</span><span class="w"> </span><span class="n">module_name</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">315</span><span class="cl"><span class="w">        </span><span class="s">&#34;hook_io&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="no">HOOK_IO_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">316</span><span class="cl"><span class="w">        </span><span class="s">&#34;hook_logging&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="no">HOOK_LOGGING_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">317</span><span class="cl"><span class="w">        </span><span class="s">&#34;config_loader&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="no">CONFIG_LOADER_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">318</span><span class="cl"><span class="w">        </span><span class="s">&#34;git_utils&#34;</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="no">GIT_UTILS_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">content</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">319</span><span class="cl"><span class="w">        </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">320</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">321</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">322</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">323</span><span class="cl"><span class="w"></span><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">324</span><span class="cl"><span class="c1">// Python Module Definition
</span></span></span><span class="line"><span class="ln">325</span><span class="cl"><span class="c1">// ============================================================================
</span></span></span><span class="line"><span class="ln">326</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">327</span><span class="cl"><span class="w"></span><span class="sd">/// Rust-accelerated hook validator with pre-compiled regex patterns
</span></span></span><span class="line"><span class="ln">328</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">329</span><span class="cl"><span class="sd">/// This module provides significant performance improvements over pure Python
</span></span></span><span class="line"><span class="ln">330</span><span class="cl"><span class="sd">/// regex for validating Claude Code hook files. Key features:
</span></span></span><span class="line"><span class="ln">331</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln">332</span><span class="cl"><span class="sd">/// - Pre-compiled regex patterns using once_cell
</span></span></span><span class="line"><span class="ln">333</span><span class="cl"><span class="sd">/// - RegexSet for efficient multi-pattern matching
</span></span></span><span class="line"><span class="ln">334</span><span class="cl"><span class="sd">/// - Batch validation API for multiple files
</span></span></span><span class="line"><span class="ln">335</span><span class="cl"><span class="sd">/// - Guaranteed linear time complexity (DFA engine)
</span></span></span><span class="line"><span class="ln">336</span><span class="cl"><span class="sd"></span><span class="cp">#[pymodule]</span><span class="w">
</span></span></span><span class="line"><span class="ln">337</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">hook_validator_rs</span><span class="p">(</span><span class="n">m</span>: <span class="kp">&amp;</span><span class="nc">Bound</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="p">,</span><span class="w"> </span><span class="n">PyModule</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">338</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">check_imports</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">339</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">is_valid_hook_name</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">340</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">get_matched_patterns</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">341</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">validate_batch</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">342</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">has_import</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">343</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_class</span>::<span class="o">&lt;</span><span class="n">ImportCheckResult</span><span class="o">&gt;</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">344</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_class</span>::<span class="o">&lt;</span><span class="n">BatchValidationResult</span><span class="o">&gt;</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">345</span><span class="cl"><span class="w">    </span><span class="nb">Ok</span><span class="p">(())</span><span class="w">
</span></span></span><span class="line"><span class="ln">346</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h3 id="建置與測試">建置與測試</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Build the extension</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin develop --release
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># Run tests</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">python -c <span class="s2">&#34;
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s2">import hook_validator_rs as rs
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s2"># Test basic import checking
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s2">content = &#39;&#39;&#39;
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s2">from hook_io import read_hook_input, write_hook_output
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s2">from hook_logging import setup_hook_logging
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s2">&#39;&#39;&#39;
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s2">result = rs.check_imports(content)
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s2">print(f&#39;Import check: {result}&#39;)
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s2">print(f&#39;Has hook_io: {result.has_hook_io}&#39;)
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="s2">print(f&#39;Uses recommended output: {result.uses_recommended_output()}&#39;)
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="s2"># Test filename validation
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="s2">print(f&#39;Valid name \&#34;check-permissions.py\&#34;: {rs.is_valid_hook_name(\&#34;check-permissions.py\&#34;)}&#39;)
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="s2">print(f&#39;Valid name \&#34;BadName.py\&#34;: {rs.is_valid_hook_name(\&#34;BadName.py\&#34;)}&#39;)
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="s2">&#34;</span></span></span></code></pre></div><h3 id="效能比較">效能比較</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">  1</span><span class="cl"><span class="s2">&#34;&#34;&#34;Performance comparison: Python re vs Rust regex&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">  2</span><span class="cl">
</span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="ln">  6</span><span class="cl">
</span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="k">def</span> <span class="nf">benchmark</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">iterations</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10000</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">  8</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Run benchmark and return average time in microseconds&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">  9</span><span class="cl">    <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl">    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">iterations</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 11</span><span class="cl">        <span class="n">func</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 12</span><span class="cl">    <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
</span></span><span class="line"><span class="ln"> 13</span><span class="cl">    <span class="n">avg_us</span> <span class="o">=</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">iterations</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1_000_000</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">avg_us</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2"> us/iteration (</span><span class="si">{</span><span class="n">iterations</span><span class="si">}</span><span class="s2"> iterations)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 15</span><span class="cl">    <span class="k">return</span> <span class="n">avg_us</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl">
</span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="c1"># Test content (typical hook file)</span>
</span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="n">TEST_CONTENT</span> <span class="o">=</span> <span class="s1">&#39;&#39;&#39;
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="s1">#!/usr/bin/env python3
</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="s1">&#34;&#34;&#34;Example hook for testing performance&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="s1">import json
</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="s1">import sys
</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="s1">from pathlib import Path
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="s1">from hook_io import read_hook_input, write_hook_output
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="s1">from hook_logging import setup_hook_logging
</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="s1">from config_loader import load_config
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="s1">def main():
</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="s1">    logger = setup_hook_logging(&#34;example-hook&#34;)
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="s1">    hook_input = read_hook_input()
</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="s1">    # Process input
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="s1">    result = {&#34;decision&#34;: &#34;approve&#34;}
</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="s1">    write_hook_output(result)
</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="s1">if __name__ == &#34;__main__&#34;:
</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="s1">    main()
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="s1">&#39;&#39;&#39;</span>
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">
</span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="c1"># Python patterns</span>
</span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="n">HOOK_IO_PATTERNS_PY</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 45</span><span class="cl">    <span class="sa">r</span><span class="s2">&#34;from\s+hook_io\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">    <span class="sa">r</span><span class="s2">&#34;from\s+lib\.hook_io\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="n">HOOK_LOGGING_PATTERNS_PY</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl">    <span class="sa">r</span><span class="s2">&#34;from\s+hook_logging\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">    <span class="sa">r</span><span class="s2">&#34;from\s+lib\.hook_logging\s+import&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 52</span><span class="cl">
</span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="k">def</span> <span class="nf">python_check</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 54</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Pure Python regex check&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 55</span><span class="cl">    <span class="n">has_hook_io</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 56</span><span class="cl">        <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">TEST_CONTENT</span><span class="p">)</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">HOOK_IO_PATTERNS_PY</span>
</span></span><span class="line"><span class="ln"> 57</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln"> 58</span><span class="cl">    <span class="n">has_logging</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl">        <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">TEST_CONTENT</span><span class="p">)</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">HOOK_LOGGING_PATTERNS_PY</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln"> 61</span><span class="cl">    <span class="k">return</span> <span class="n">has_hook_io</span><span class="p">,</span> <span class="n">has_logging</span>
</span></span><span class="line"><span class="ln"> 62</span><span class="cl">
</span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="k">def</span> <span class="nf">python_check_compiled</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 64</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Python regex with pre-compiled patterns&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl">    <span class="k">global</span> <span class="n">_compiled_hook_io</span><span class="p">,</span> <span class="n">_compiled_logging</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">    <span class="n">has_hook_io</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">TEST_CONTENT</span><span class="p">)</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">_compiled_hook_io</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 67</span><span class="cl">    <span class="n">has_logging</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">TEST_CONTENT</span><span class="p">)</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">_compiled_logging</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">    <span class="k">return</span> <span class="n">has_hook_io</span><span class="p">,</span> <span class="n">has_logging</span>
</span></span><span class="line"><span class="ln"> 69</span><span class="cl">
</span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="c1"># Pre-compile Python patterns</span>
</span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="n">_compiled_hook_io</span> <span class="o">=</span> <span class="p">[</span><span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">HOOK_IO_PATTERNS_PY</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="n">_compiled_logging</span> <span class="o">=</span> <span class="p">[</span><span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">HOOK_LOGGING_PATTERNS_PY</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">
</span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="k">def</span> <span class="nf">rust_check</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Rust regex check&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">    <span class="kn">import</span> <span class="nn">hook_validator_rs</span> <span class="k">as</span> <span class="nn">rs</span>
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">rs</span><span class="o">.</span><span class="n">check_imports</span><span class="p">(</span><span class="n">TEST_CONTENT</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">    <span class="k">return</span> <span class="n">result</span><span class="o">.</span><span class="n">has_hook_io</span><span class="p">,</span> <span class="n">result</span><span class="o">.</span><span class="n">has_hook_logging</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">
</span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Performance Comparison: Python re vs Rust regex&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 84</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Content size: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">TEST_CONTENT</span><span class="p">)</span><span class="si">}</span><span class="s2"> bytes</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">
</span></span><span class="line"><span class="ln"> 86</span><span class="cl">    <span class="c1"># Warm up</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl">    <span class="n">python_check</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">    <span class="n">python_check_compiled</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">    <span class="n">rust_check</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">    <span class="c1"># Benchmark</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">    <span class="n">py_time</span> <span class="o">=</span> <span class="n">benchmark</span><span class="p">(</span><span class="s2">&#34;Python re (uncompiled)&#34;</span><span class="p">,</span> <span class="n">python_check</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 93</span><span class="cl">    <span class="n">py_compiled_time</span> <span class="o">=</span> <span class="n">benchmark</span><span class="p">(</span><span class="s2">&#34;Python re (compiled)&#34;</span><span class="p">,</span> <span class="n">python_check_compiled</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">    <span class="n">rust_time</span> <span class="o">=</span> <span class="n">benchmark</span><span class="p">(</span><span class="s2">&#34;Rust regex&#34;</span><span class="p">,</span> <span class="n">rust_check</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">
</span></span><span class="line"><span class="ln"> 96</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="o">+</span> <span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Results Summary&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Python uncompiled: </span><span class="si">{</span><span class="n">py_time</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2"> us&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">100</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Python compiled:   </span><span class="si">{</span><span class="n">py_compiled_time</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2"> us&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">101</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Rust regex:        </span><span class="si">{</span><span class="n">rust_time</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2"> us&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">102</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Speedup vs uncompiled: </span><span class="si">{</span><span class="n">py_time</span> <span class="o">/</span> <span class="n">rust_time</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">x&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Speedup vs compiled:   </span><span class="si">{</span><span class="n">py_compiled_time</span> <span class="o">/</span> <span class="n">rust_time</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">x&#34;</span><span class="p">)</span></span></span></code></pre></div><p>典型結果：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">============================================================
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">Performance Comparison: Python re vs Rust regex
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">============================================================
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">Content size: 512 bytes
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Python re (uncompiled): 12.45 us/iteration (10000 iterations)
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">Python re (compiled):    4.32 us/iteration (10000 iterations)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">Rust regex:              0.89 us/iteration (10000 iterations)
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">============================================================
</span></span><span class="line"><span class="ln">11</span><span class="cl">Results Summary
</span></span><span class="line"><span class="ln">12</span><span class="cl">============================================================
</span></span><span class="line"><span class="ln">13</span><span class="cl">Python uncompiled: 12.45 us
</span></span><span class="line"><span class="ln">14</span><span class="cl">Python compiled:    4.32 us
</span></span><span class="line"><span class="ln">15</span><span class="cl">Rust regex:         0.89 us
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">Speedup vs uncompiled: 14.0x
</span></span><span class="line"><span class="ln">18</span><span class="cl">Speedup vs compiled:   4.9x</span></span></code></pre></div><h4 id="病態輸入效能比較">病態輸入效能比較</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="s2">&#34;&#34;&#34;Pathological input benchmark - demonstrating DFA vs backtracking&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">def</span> <span class="nf">test_catastrophic_backtracking</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s2">    Test pattern that causes catastrophic backtracking in NFA engines
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s2">    Pattern: (a+)+b
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s2">    Input: &#34;aaa...a&#34; (no &#39;b&#39; at end)
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s2">    Python re: O(2^n) time complexity
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s2">    Rust regex: O(n) time complexity (DFA engine)
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">pattern</span> <span class="o">=</span> <span class="sa">r</span><span class="s2">&#34;(a+)+b&#34;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Catastrophic Backtracking Test&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Pattern: (a+)+b&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;-&#34;</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">15</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">25</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="n">text</span> <span class="o">=</span> <span class="s2">&#34;a&#34;</span> <span class="o">*</span> <span class="n">n</span> <span class="o">+</span> <span class="s2">&#34;c&#34;</span>  <span class="c1"># No match - triggers backtracking</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="c1"># Python test</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">            <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">text</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="k">except</span> <span class="ne">TimeoutError</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">            <span class="n">py_time</span> <span class="o">=</span> <span class="s2">&#34;&gt;5s (timeout)&#34;</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">            <span class="n">py_time</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span><span class="o">*</span><span class="mi">1000</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2">ms&#34;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="c1"># Note: Rust regex doesn&#39;t support backreferences,</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">        <span class="c1"># so (a+)+b is rewritten as a+b internally</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="c1"># This demonstrates why Rust regex is safe from this attack</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;n=</span><span class="si">{</span><span class="n">n</span><span class="si">:</span><span class="s2">2d</span><span class="si">}</span><span class="s2">: Python=</span><span class="si">{</span><span class="n">py_time</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="k">def</span> <span class="nf">test_regex_dos</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="s2">    Test ReDoS (Regular Expression Denial of Service) patterns
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">    <span class="c1"># Common ReDoS patterns</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="n">redos_patterns</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">        <span class="p">(</span><span class="sa">r</span><span class="s2">&#34;(a+)+$&#34;</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span> <span class="o">*</span> <span class="mi">20</span> <span class="o">+</span> <span class="s2">&#34;!&#34;</span><span class="p">),</span>          <span class="c1"># Nested quantifiers</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">        <span class="p">(</span><span class="sa">r</span><span class="s2">&#34;(a|aa)+$&#34;</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span> <span class="o">*</span> <span class="mi">20</span> <span class="o">+</span> <span class="s2">&#34;!&#34;</span><span class="p">),</span>        <span class="c1"># Overlapping alternatives</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">        <span class="p">(</span><span class="sa">r</span><span class="s2">&#34;(.*a)</span><span class="si">{10}</span><span class="s2">$&#34;</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">+</span> <span class="s2">&#34;!&#34;</span><span class="p">),</span>      <span class="c1"># Repeated wildcards</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">ReDoS Pattern Tests&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;-&#34;</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">
</span></span><span class="line"><span class="ln">54</span><span class="cl">    <span class="k">for</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">text</span> <span class="ow">in</span> <span class="n">redos_patterns</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">        <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">        <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Pattern: </span><span class="si">{</span><span class="n">pattern</span><span class="si">:</span><span class="s2">20s</span><span class="si">}</span><span class="s2"> Time: </span><span class="si">{</span><span class="n">elapsed</span><span class="o">*</span><span class="mi">1000</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2">ms&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">
</span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">    <span class="n">test_catastrophic_backtracking</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">    <span class="n">test_regex_dos</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">
</span></span><span class="line"><span class="ln">64</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="o">+</span> <span class="s2">&#34;=&#34;</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Note: Rust regex crate uses DFA/hybrid engine&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;that guarantees O(n) time complexity for all inputs.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;It does NOT support backreferences, which prevents&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;catastrophic backtracking by design.&#34;</span><span class="p">)</span></span></span></code></pre></div><h2 id="設計權衡">設計權衡</h2>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Python re</th>
          <th>Rust regex</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>引擎類型</strong></td>
          <td>NFA with backtracking</td>
          <td>DFA/混合引擎</td>
      </tr>
      <tr>
          <td><strong>時間複雜度</strong></td>
          <td>最壞 O(2^n)</td>
          <td>保證 O(n)</td>
      </tr>
      <tr>
          <td><strong>功能完整性</strong></td>
          <td>完整（lookahead、backreference）</td>
          <td>部分限制（無 backreference）</td>
      </tr>
      <tr>
          <td><strong>整合難度</strong></td>
          <td>無（內建）</td>
          <td>需要 FFI（PyO3 + Maturin）</td>
      </tr>
      <tr>
          <td><strong>除錯便利</strong></td>
          <td>Python 原生</td>
          <td>需要 Rust 工具鏈</td>
      </tr>
      <tr>
          <td><strong>記憶體安全</strong></td>
          <td>GC 管理</td>
          <td>編譯時保證</td>
      </tr>
      <tr>
          <td><strong>多執行緒</strong></td>
          <td>受 GIL 限制</td>
          <td>完全平行化</td>
      </tr>
      <tr>
          <td><strong>SIMD 加速</strong></td>
          <td>無</td>
          <td>自動啟用</td>
      </tr>
  </tbody>
</table>
<h3 id="rust-regex-不支援的功能">Rust regex 不支援的功能</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// These patterns will fail to compile in Rust regex:
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="c1">// 1. Backreferences
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// r&#34;(\w+)\s+\1&#34;  // ERROR: backreference not supported
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="c1">// 2. Lookahead/Lookbehind
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">// r&#34;(?=foo)&#34;    // ERROR: lookahead not supported
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">// r&#34;(?&lt;=foo)&#34;   // ERROR: lookbehind not supported
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="c1">// 3. Atomic groups
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">// r&#34;(?&gt;foo)&#34;    // ERROR: atomic groups not supported
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"></span><span class="c1">// Workaround: Use regex-fancy crate for these features
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// (with performance trade-offs)
</span></span></span></code></pre></div><h2 id="什麼時候該用-rust-regex">什麼時候該用 Rust regex？</h2>
<h3 id="適合使用">適合使用</h3>
<ul>
<li><strong>大量文字需要驗證</strong>：日誌分析、程式碼審查、批次處理</li>
<li><strong>正則表達式可能有病態輸入</strong>：用戶提供的輸入、不可信來源</li>
<li><strong>需要保證線性時間</strong>：安全性要求、SLA 保證</li>
<li><strong>高併發場景</strong>：多執行緒處理、Web 服務</li>
<li><strong>效能關鍵路徑</strong>：CI/CD pipeline、即時驗證</li>
</ul>
<h3 id="不建議使用">不建議使用</h3>
<ul>
<li><strong>需要 lookahead/lookbehind</strong>：複雜的文字邊界檢查</li>
<li><strong>需要 backreference</strong>：重複單詞檢測、HTML 標籤匹配</li>
<li><strong>驗證次數很少</strong>：一次性腳本、開發階段</li>
<li><strong>模式簡單</strong>：固定字串、簡單前綴/後綴檢查</li>
<li><strong>團隊不熟悉 Rust</strong>：維護成本可能超過效能收益</li>
</ul>
<h2 id="練習">練習</h2>
<h3 id="1-基礎練習email-驗證">1. 基礎練習：Email 驗證</h3>
<p>用 Rust regex 實作 email 地址驗證：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Exercise: Implement email validation
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">once_cell</span>::<span class="n">sync</span>::<span class="n">Lazy</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">regex</span>::<span class="n">Regex</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">EMAIL_REGEX</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">Regex</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="c1">// TODO: Implement email pattern
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// Requirements:
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// - Local part: alphanumeric + dots + underscores + hyphens
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// - @ symbol
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// - Domain: alphanumeric + dots + hyphens
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// - TLD: 2-6 alphabetic characters
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="sa">r</span><span class="s">&#34;TODO&#34;</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid email regex&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">is_valid_email</span><span class="p">(</span><span class="n">email</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">bool</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="no">EMAIL_REGEX</span><span class="p">.</span><span class="n">is_match</span><span class="p">(</span><span class="n">email</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w"></span><span class="c1">// Test cases:
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1">// is_valid_email(&#34;user@example.com&#34;) -&gt; true
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1">// is_valid_email(&#34;user.name+tag@example.co.uk&#34;) -&gt; true
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1">// is_valid_email(&#34;invalid@&#34;) -&gt; false
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1">// is_valid_email(&#34;@example.com&#34;) -&gt; false
</span></span></span></code></pre></div><h3 id="2-進階練習regexset-批次匹配">2. 進階練習：RegexSet 批次匹配</h3>
<p>實作一個程式語言檢測器，判斷程式碼片段是哪種語言：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Exercise: Language detection using RegexSet
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">once_cell</span>::<span class="n">sync</span>::<span class="n">Lazy</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">regex</span>::<span class="n">RegexSet</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">collections</span>::<span class="n">HashMap</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">LANGUAGE_PATTERNS</span>: <span class="nc">Lazy</span><span class="o">&lt;</span><span class="n">RegexSet</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Lazy</span>::<span class="n">new</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="n">RegexSet</span>::<span class="n">new</span><span class="p">([</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="c1">// TODO: Add patterns for different languages
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="c1">// 0: Python (def, import, from ... import)
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="c1">// 1: JavaScript (const, let, =&gt;)
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="c1">// 2: Rust (fn, let mut, impl)
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span><span class="w">        </span><span class="c1">// 3: Go (func, package, import)
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="p">]).</span><span class="n">expect</span><span class="p">(</span><span class="s">&#34;Invalid language patterns&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"></span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"></span><span class="k">static</span><span class="w"> </span><span class="no">LANGUAGE_NAMES</span>: <span class="p">[</span><span class="o">&amp;</span><span class="kt">str</span><span class="p">;</span><span class="w"> </span><span class="mi">4</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s">&#34;Python&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;JavaScript&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Rust&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Go&#34;</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">detect_languages</span><span class="p">(</span><span class="n">code</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="c1">// TODO: Return list of detected languages
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// Hint: Use LANGUAGE_PATTERNS.matches(code)
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="fm">vec!</span><span class="p">[]</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w"></span><span class="c1">// Test case:
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1">// detect_languages(&#34;def hello():\n    print(&#39;Hi&#39;)&#34;) -&gt; [&#34;Python&#34;]
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1">// detect_languages(&#34;const x = () =&gt; {}&#34;) -&gt; [&#34;JavaScript&#34;]
</span></span></span></code></pre></div><h3 id="3-挑戰題病態輸入防護">3. 挑戰題：病態輸入防護</h3>
<p>設計一個安全的正則表達式驗證器，拒絕可能導致 ReDoS 的模式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Challenge: ReDoS-safe regex validator
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="sd">/// Validate that a regex pattern is safe from ReDoS attacks
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="sd">/// Unsafe patterns to detect:
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="sd">/// 1. Nested quantifiers: (a+)+
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="sd">/// 2. Overlapping alternatives: (a|a)+
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="sd">/// 3. Long quantified groups with wildcards: (.*)+
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">is_safe_pattern</span><span class="p">(</span><span class="n">pattern</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="c1">// Strategy 1: Try to compile with Rust regex
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// Rust regex rejects inherently unsafe patterns
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="k">match</span><span class="w"> </span><span class="n">regex</span>::<span class="n">Regex</span>::<span class="n">new</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="nb">Ok</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="kc">true</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="nb">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">            </span><span class="c1">// Check if error is due to unsupported features
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="w">            </span><span class="c1">// vs actual syntax errors
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">error_msg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">e</span><span class="p">.</span><span class="n">to_string</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="n">error_msg</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="s">&#34;backreference&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">                </span><span class="o">||</span><span class="w"> </span><span class="n">error_msg</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="s">&#34;look&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">                </span><span class="c1">// Potentially unsafe pattern
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span><span class="w">                </span><span class="nb">Ok</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">                </span><span class="c1">// Syntax error
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span><span class="w">                </span><span class="nb">Err</span><span class="p">(</span><span class="n">pyo3</span>::<span class="n">exceptions</span>::<span class="n">PyValueError</span>::<span class="n">new_err</span><span class="p">(</span><span class="n">error_msg</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w"></span><span class="sd">/// Benchmark a pattern to detect slow execution
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">benchmark_pattern</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">    </span><span class="n">pattern</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">    </span><span class="n">test_input</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">    </span><span class="n">max_ms</span>: <span class="kt">u64</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">    </span><span class="c1">// TODO: Implement timeout-based safety check
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// 1. Compile the pattern
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// 2. Run match with timeout
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="c1">// 3. Return false if exceeds max_ms
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1"></span><span class="w">    </span><span class="nb">Ok</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div><h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://docs.rs/regex/">Rust regex crate 文件</a> - 完整的 API 文件與效能說明</li>
<li><a href="https://swtch.com/~rsc/regexp/">正則表達式引擎比較</a> - Russ Cox 的經典系列文章</li>
<li><a href="https://pyo3.rs/">PyO3 User Guide</a> - PyO3 完整教學</li>
<li><a href="https://docs.rs/once_cell/">once_cell crate</a> - 延遲初始化最佳實踐</li>
<li><a href="https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS">ReDoS 攻擊與防護</a> - OWASP 安全指南</li>
</ul>
<hr>
<p><em>上一章：<a href="/blog/python-advanced/06-rust-extensions/case-studies/pyo3-parser/" data-link-title="案例：PyO3 文字解析" data-link-desc="用 PyO3 和 Rust 實現高效能的 Markdown 連結解析器">PyO3 文字解析</a></em>
<em>返回：<a href="/blog/python-advanced/06-rust-extensions/" data-link-title="模組六：用 Rust 擴展 Python" data-link-desc="學習使用 PyO3 和 Maturin 用 Rust 擴展 Python">模組六：用 Rust 擴展 Python</a></em></p>
]]></content:encoded></item></channel></rss>