Skip to content

Instantly share code, notes, and snippets.

@rybla
Last active December 11, 2025 17:33
Show Gist options
  • Select an option

  • Save rybla/8a810e5f32af42e64af90f7a2ae43faf to your computer and use it in GitHub Desktop.

Select an option

Save rybla/8a810e5f32af42e64af90f7a2ae43faf to your computer and use it in GitHub Desktop.
In Rust, using Maud library to generate a single-page HTML app for fuzzy-filtering a list read from a text file
[package]
name = "maud-example-fuzzy-filter-list"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.100"
maud = "0.27.0"
Haskell
Rust
TypeScript
Agda
PureScript
Python
OCaml
Scala
Erlang
Elixir
Clojure
F#
Racket
Scheme
Common Lisp
Coq
Idris
Lean
Mercury
Standard ML
/*
ORGANIZATION AND LOGIC:
This program demonstrates a Static Site Generator (SSG) pattern using Rust and the Maud library.
1. Data Ingestion: The program reads a plaintext file (`items.txt`) expected to be in the current directory.
It uses standard `std::fs` and iterator methods to parse non-empty lines into a vector of strings.
2. HTML Generation (Maud):
- We use the `maud::html!` macro to construct the DOM. This macro compiles HTML directly into Rust code
for performance and type safety.
- CSS is embedded directly in the `<head>` to keep the output as a single, self-contained file.
- The list of items is rendered dynamically using a Rust `for` loop inside the `html!` macro.
3. Interactivity (JavaScript):
- Since the Rust program runs ahead-of-time to generate the file, the "fuzzy filter" logic is implemented
in client-side JavaScript embedded in the `script` tag.
- The "fuzzy" algorithm used here is a Subsequence Match (characters must appear in order, but not necessarily adjacent).
- An event listener on the search input toggles the `display` style of list items based on the match result.
4. Output: The resulting HTML markup is written to `index.html`.
*/
use anyhow::Result;
use maud::{DOCTYPE, PreEscaped, html};
use std::fs;
use std::io::Write;
fn main() -> Result<()> {
let content = fs::read_to_string("items.txt")?;
let items: Vec<&str> = content.lines().filter(|l| !l.is_empty()).collect();
let markup = html! {
(DOCTYPE)
html lang="en" {
head {
meta charset="utf-8";
meta name="viewport" content="width=device-width, initial-scale=1.0";
title { "Rust + Maud Filter" }
style { (PreEscaped(r#"
body { font-family: system-ui, sans-serif; max-width: 600px; margin: 2rem auto; padding: 0 1rem; background: #f4f4f5; color: #18181b; }
h1 { font-size: 1.5rem; margin-bottom: 1rem; }
input { width: 100%; padding: 0.75rem; font-size: 1rem; border: 1px solid #d4d4d8; border-radius: 0.5rem; margin-bottom: 1.5rem; box-sizing: border-box; }
ul { list-style: none; padding: 0; background: white; border-radius: 0.5rem; box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1); overflow: hidden; }
li { padding: 0.75rem 1rem; border-bottom: 1px solid #f4f4f5; }
li:last-child { border-bottom: none; }
.hidden { display: none; }
"#)) }
}
body {
h1 { "Item Filter" }
input type="text" id="search" placeholder="Fuzzy search items...";
ul id="list" {
@for item in &items {
li { (item) }
}
}
script { (PreEscaped(r#"
const searchInput = document.getElementById('search');
const listItems = document.querySelectorAll('#list li');
// Simple subsequence fuzzy match
function isFuzzyMatch(text, query) {
text = text.toLowerCase();
query = query.toLowerCase();
let queryIdx = 0;
for (let char of text) {
if (char === query[queryIdx]) queryIdx++;
if (queryIdx === query.length) return true;
}
return false;
}
searchInput.addEventListener('input', (e) => {
const query = e.target.value;
listItems.forEach(li => {
if (isFuzzyMatch(li.textContent, query)) {
li.classList.remove('hidden');
} else {
li.classList.add('hidden');
}
});
});
"#)) }
}
}
};
let mut file = fs::File::create("index.html")?;
write!(file, "{}", markup.into_string())?;
// file.write_all(markup.into_string().as_bytes())?;
println!(
"Successfully generated index.html with {} items.",
items.len()
);
Ok(())
}
@rybla
Copy link
Author

rybla commented Dec 11, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment