rs_web/
lib.rs

1//! # rs-web - Static Site Generator
2//!
3//! A fast, opinionated static site generator built in Rust with support for:
4//!
5//! - **Markdown processing** with syntax highlighting, external link handling
6//! - **Content encryption** via Lua (`rs.crypt` module)
7//! - **Link graph** with backlinks and visualization (Obsidian-style)
8//! - **RSS feed** generation with section filtering
9//! - **Parallel processing** for fast builds
10//! - **Live reload** with automatic browser refresh during watch mode
11//! - **Asset minification** for CSS, JS (with dead code elimination), and HTML
12//!
13//! ## Quick Start
14//!
15//! ```bash
16//! # Build the site
17//! rs-web build
18//!
19//! # Build to custom output directory
20//! rs-web build --output public
21//!
22//! # Watch for changes and rebuild incrementally with live reload
23//! rs-web build --watch
24//!
25//! # Watch mode with custom port
26//! rs-web build --watch --port 8080
27//!
28//! # Enable debug logging
29//! rs-web --debug build
30//!
31//! # Set specific log level (trace, debug, info, warning, error)
32//! rs-web --log-level trace build
33//!
34//! # Or use environment variable
35//! RS_WEB_LOG_LEVEL=debug rs-web build
36//! ```
37//!
38//! ## Configuration
39//!
40//! Configure via `config.lua`:
41//!
42//! ```lua
43//! return {
44//!   site = {
45//!     title = "My Site",
46//!     description = "Site description",
47//!     base_url = "https://example.com",
48//!     author = "Your Name",
49//!   },
50//!
51//!   build = {
52//!     output_dir = "dist",
53//!   },
54//!
55//!   -- Generate pages via Lua
56//!   pages = function()
57//!     return {
58//!       { path = "/", template = "home.html", title = "Home" },
59//!       { path = "/about/", template = "page.html", title = "About", minify = true },
60//!     }
61//!   end,
62//!
63//!   -- Build hooks
64//!   hooks = {
65//!     before_build = function() print("Starting...") end,
66//!     after_build = function() print("Done!") end,
67//!   },
68//! }
69//! ```
70//!
71//! ### Configuration Sections
72//!
73//! - `site` - Required: title, description, base_url, author
74//! - `seo` - twitter_handle, default_og_image
75//! - `build` - output_dir
76//! - `paths` - templates
77//!
78//! ### Lua Sandbox
79//!
80//! By default, file operations are sandboxed to the project directory.
81//! To disable (use with caution):
82//!
83//! ```lua
84//! return {
85//!   lua = { sandbox = false },
86//!   site = { ... },
87//! }
88//! ```
89//!
90//! ### Lua API Functions
91//!
92//! **File Operations:**
93//! - `read_file(path)` - Read file contents
94//! - `write_file(path, content)` - Write content to file
95//! - `copy_file(src, dest)` - Copy file (binary-safe)
96//! - `file_exists(path)` - Check if file exists
97//! - `list_files(path, pattern?)` - List files matching pattern
98//! - `list_dirs(path)` - List subdirectories
99//! - `load_json(path)` - Load and parse JSON file
100//! - `load_yaml(path)` - Load and parse YAML file
101//! - `load_toml(path)` - Load and parse TOML file
102//! - `read_frontmatter(path)` - Extract frontmatter and content from markdown
103//!
104//! **Content Processing:**
105//! - `html_to_text(html)` - Convert HTML to plain text
106//!
107//! **Date & Time (rs.date):**
108//! - `now()` - Get current Unix timestamp
109//! - `from_timestamp(ts)` - Convert Unix timestamp to DateTime table
110//! - `to_timestamp(date)` - Convert date to Unix timestamp
111//! - `format(date, format)` - Format date using strftime
112//! - `parse(str, format?)` - Parse date string (auto-detects or custom format)
113//! - `rss_format(date)` - Format for RSS (RFC 2822)
114//! - `iso_format(date)` - Format as ISO 8601
115//! - `add(date, delta)` - Add time to date
116//! - `diff(date1, date2)` - Difference in seconds
117//!
118//! **Markdown (rs.markdown):**
119//! - `render(content, opts?)` - Render markdown to HTML with optional plugins
120//! - `plugins(...)` - Combine/flatten plugins
121//! - `plugins.default(opts?)` - Get default plugins (lazy_images, heading_anchors, external_links)
122//! - `plugins.lazy_images(opts?)` - Plugin: add loading="lazy" decoding="async" to images
123//! - `plugins.heading_anchors(opts?)` - Plugin: add id="slug" to headings
124//! - `plugins.external_links(opts?)` - Plugin: add target="_blank" rel="noopener" to external links
125//!
126//! **Image Processing:**
127//! - `image_dimensions(path)` - Get image width and height
128//! - `image_resize(input, output, options)` - Resize image
129//! - `image_convert(input, output, options?)` - Convert image format
130//! - `image_optimize(input, output, options?)` - Optimize/compress image
131//!
132//! **Fonts (rs.fonts):**
133//! - `download_google_font(family, options)` - Download Google Font with optional CSS minification (async)
134//!
135//! **JS Module (rs.js):**
136//! - `concat(paths, output, options?)` - Concatenate JS files with optional minification (async)
137//! - `bundle(entry, output, options?)` - Bundle JS with imports via Rolldown (async)
138//! - `bundle_many(entries, output_dir, options?)` - Bundle multiple JS entries to separate files (async)
139//!
140//! **CSS Module (rs.css):**
141//! - `concat(paths, output, options?)` - Concatenate CSS files with optional minification (async)
142//! - `bundle(paths, output, options?)` - Bundle CSS with @import resolution via LightningCSS (async)
143//! - `bundle_many(paths, output_dir, options?)` - Bundle multiple CSS entries to separate files (async)
144//! - `purge(css_path, options?)` - Remove unused CSS rules based on HTML/JS output (async, call in after_build)
145//! - `critical(html, css_path, options?)` - Extract critical CSS for a specific HTML page (async)
146//! - `inline_critical(html_path, css_path, options?)` - Inline critical CSS into HTML with async loading (async)
147//!
148//! **Asset Hashing (rs.assets):**
149//! - `hash(content, length?)` - Compute SHA256 hash of content (async)
150//! - `hash_sync(content, length?)` - Compute hash synchronously
151//! - `write_hashed(content, path, options?)` - Write file with hashed filename (async)
152//! - `register(original, hashed)` - Register asset path mapping
153//! - `get_path(path)` - Get hashed path for original
154//! - `manifest()` - Get all path mappings
155//! - `clear()` - Clear the manifest
156//!
157//! **PWA (rs.pwa):**
158//! - `manifest(options)` - Generate web app manifest.json (async)
159//! - `service_worker(options)` - Generate service worker sw.js (async)
160//!
161//! **SEO (rs.seo):**
162//! - `sitemap(options)` - Generate XML sitemap (async)
163//! - `robots(options)` - Generate robots.txt (async)
164//!
165//! **Text Processing:**
166//! - `slugify(text)` - Convert text to URL-friendly slug
167//! - `word_count(text)` - Count words in text
168//! - `reading_time(text, wpm?)` - Calculate reading time in minutes
169//! - `truncate(text, len, suffix?)` - Truncate text with optional suffix
170//! - `strip_tags(html)` - Remove HTML tags
171//! - `hash(content)` - Hash content (xxHash64)
172//! - `hash_file(path)` - Hash file contents
173//! - `url_encode(str)` - URL encode a string
174//! - `url_decode(str)` - URL decode a string
175//!
176//! **Path Utilities:**
177//! - `join_path(...)` - Join path segments
178//! - `basename(path)` - Get file name from path
179//! - `dirname(path)` - Get directory from path
180//! - `extension(path)` - Get file extension
181//!
182//! **Collections:**
183//! - `filter(items, fn)` - Filter items where fn returns true
184//! - `sort(items, fn)` - Sort items using comparator
185//! - `map(items, fn)` - Transform each item
186//! - `find(items, fn)` - Find first item where fn returns true
187//! - `group_by(items, key_fn)` - Group items by key
188//! - `unique(items)` - Remove duplicates
189//! - `reverse(items)` - Reverse array order
190//! - `take(items, n)` - Take first n items
191//! - `skip(items, n)` - Skip first n items
192//! - `keys(table)` - Get all keys from a table
193//! - `values(table)` - Get all values from a table
194//!
195//! **Environment:**
196//! - `env(name, default?)` - Get environment variable with optional default
197//! - `print(...)` - Log output to build log
198//! - `git_info(path?)` - Get git info (hash, branch, author, timestamp, dirty)
199//!
200//! **Parallel (rs.parallel):** Rayon-backed true parallel operations
201//! - `load_json(paths)` / `load_yaml(paths)` - Load multiple files in parallel
202//! - `read_files(paths)` / `read_frontmatter(paths)` - Read multiple files
203//! - `create_dirs(paths)` / `copy_files(sources, dests)` - Parallel file operations
204//! - `image_convert(sources, dests, opts?)` - Convert images in parallel
205//! - `map(items, fn, ctx?)` / `filter(items, fn, ctx?)` - Parallel map/filter with explicit context
206//! - `map_seq(items, fn)` / `filter_seq(items, fn)` - Sequential fallbacks for non-serializable items
207//!
208//! **Async I/O (rs.async):** All return handles, await with `rs.async.await(task)` or `rs.async.await_all(tasks)`
209//! - `fetch(url, opts?)` / `fetch_bytes(url, opts?)` - HTTP fetch (text/binary)
210//! - `fetch_sync(url, opts?)` - Blocking fetch (returns response directly)
211//! - `write_file(path, content)` / `write(path, bytes)` - Write text/binary
212//! - `copy_file(src, dst)` / `create_dir(path)` - File/dir operations
213//! - `exists(path)` / `read_file(path)` - Check existence / read file
214//!
215//! **Encryption (rs.crypt):**
216//! - `encrypt(content, password?)` - Encrypt content (AES-256-GCM)
217//! - `decrypt(data, password?)` - Decrypt content
218//! - `encrypt_html(content, options?)` - Generate encrypted HTML block for browser decryption
219//!
220//! All file operations respect the sandbox setting and are tracked for incremental builds.
221//! Encryption uses SITE_PASSWORD environment variable if password is not provided.
222//!
223//! ## Frontmatter
224//!
225//! Post frontmatter options (YAML or TOML):
226//!
227//! ```yaml
228//! ---
229//! title: "Post Title"           # Required
230//! description: "Description"    # Optional
231//! date: 2024-01-15              # Optional (YAML date or string)
232//! tags: ["tag1", "tag2"]        # Optional
233//! draft: false                  # Optional (default: false, excluded from build)
234//! image: "/static/post.png"     # Optional: OG image
235//! template: "custom.html"       # Optional: Override template
236//! slug: "custom-slug"           # Optional: Override URL slug
237//! permalink: "/custom/url/"     # Optional: Full URL override
238//! ---
239//! ```
240//!
241//! ## Encryption
242//!
243//! Encryption is handled via the `rs.crypt` module in Lua. Use `SITE_PASSWORD`
244//! environment variable or pass password explicitly:
245//!
246//! ```lua
247//! -- Encrypt content
248//! local encrypted = rs.crypt.encrypt("secret content")
249//! -- Returns: { ciphertext, salt, nonce }
250//!
251//! -- Generate encrypted HTML block for browser decryption
252//! local html = rs.crypt.encrypt_html("secret content", {
253//!   slug = "post-slug",
254//!   block_id = "secret-1",
255//! })
256//!
257//! -- Decrypt content
258//! local plaintext = rs.crypt.decrypt(encrypted)
259//! ```
260//!
261//! ## Template Variables
262//!
263//! ### Home Template (`home.html`)
264//! - `site` - Site config (title, description, base_url, author)
265//! - `page` - Page info (title, description, url, image)
266//! - `sections` - All sections with posts (`sections.blog.posts`)
267//! - `content` - Rendered markdown content
268//!
269//! ### Post Template (`post.html`)
270//! - `site` - Site config
271//! - `post` - Post info (title, url, date, tags, reading_time, etc.)
272//! - `page` - Page info for head.html compatibility
273//! - `content` - Rendered markdown content
274//! - `backlinks` - Posts linking to this post (url, title, section)
275//! - `graph` - Local graph data (nodes, edges) for visualization
276//!
277//! ### Graph Template (`graph.html`)
278//! - `site` - Site config
279//! - `page` - Page info
280//! - `graph` - Full graph data (nodes, edges)
281//!
282//! ## Modules
283//!
284//! - [`config`] - Configuration loading and structures
285//! - [`lua`] - Lua API including `rs.markdown` for markdown processing
286//! - [`templates`] - Tera template rendering
287//! - [`encryption`] - AES-256-GCM encryption utilities (used by `rs.crypt` Lua module)
288//! - [`build`] - Main build orchestrator
289
290/// Print output at info level (always shown regardless of log level)
291#[macro_export]
292macro_rules! rs_print {
293    ($($arg:tt)*) => {
294        log::info!(target: "rs_print", "{}", format!($($arg)*))
295    };
296}
297
298pub mod assets;
299pub mod build;
300pub mod config;
301pub mod data;
302pub mod encryption;
303pub mod git;
304pub mod lua;
305pub mod server;
306pub mod templates;
307pub mod text;
308pub mod tracker;
309pub mod watch;