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//!
12//! ## Quick Start
13//!
14//! ```bash
15//! # Build the site
16//! rs-web build
17//!
18//! # Build to custom output directory
19//! rs-web build --output public
20//!
21//! # Watch for changes and rebuild incrementally with live reload
22//! rs-web build --watch
23//!
24//! # Watch mode with custom port
25//! rs-web build --watch --port 8080
26//!
27//! # Enable debug logging
28//! rs-web --debug build
29//!
30//! # Set specific log level (trace, debug, info, warning, error)
31//! rs-web --log-level trace build
32//!
33//! # Or use environment variable
34//! RS_WEB_LOG_LEVEL=debug rs-web build
35//! ```
36//!
37//! ## Configuration
38//!
39//! Configure via `config.lua`:
40//!
41//! ```lua
42//! return {
43//!   site = {
44//!     title = "My Site",
45//!     description = "Site description",
46//!     base_url = "https://example.com",
47//!     author = "Your Name",
48//!   },
49//!
50//!   build = {
51//!     output_dir = "dist",
52//!   },
53//!
54//!   -- Generate pages via Lua
55//!   pages = function()
56//!     return {
57//!       { path = "/", template = "home.html", title = "Home" },
58//!       { path = "/about/", template = "page.html", title = "About" },
59//!     }
60//!   end,
61//!
62//!   -- Build hooks
63//!   hooks = {
64//!     before_build = function() print("Starting...") end,
65//!     after_build = function() print("Done!") end,
66//!   },
67//! }
68//! ```
69//!
70//! ### Configuration Sections
71//!
72//! - `site` - Required: title, description, base_url, author
73//! - `seo` - twitter_handle, default_og_image
74//! - `build` - output_dir
75//! - `paths` - styles, static_files, templates
76//!
77//! ### Lua Sandbox
78//!
79//! By default, file operations are sandboxed to the project directory.
80//! To disable (use with caution):
81//!
82//! ```lua
83//! return {
84//!   lua = { sandbox = false },
85//!   site = { ... },
86//! }
87//! ```
88//!
89//! ### Lua API Functions
90//!
91//! **File Operations:**
92//! - `read_file(path)` - Read file contents
93//! - `write_file(path, content)` - Write content to file
94//! - `copy_file(src, dest)` - Copy file (binary-safe)
95//! - `file_exists(path)` - Check if file exists
96//! - `list_files(path, pattern?)` - List files matching pattern
97//! - `list_dirs(path)` - List subdirectories
98//! - `load_json(path)` - Load and parse JSON file
99//! - `load_yaml(path)` - Load and parse YAML file
100//! - `load_toml(path)` - Load and parse TOML file
101//! - `read_frontmatter(path)` - Extract frontmatter and content from markdown
102//!
103//! **Content Processing:**
104//! - `render_markdown(content, transform_fn?)` - Convert markdown to HTML
105//! - `html_to_text(html)` - Convert HTML to plain text
106//! - `rss_date(date_string)` - Format date for RSS (RFC 2822)
107//!
108//! **Image Processing:**
109//! - `image_dimensions(path)` - Get image width and height
110//! - `image_resize(input, output, options)` - Resize image
111//! - `image_convert(input, output, options?)` - Convert image format
112//! - `image_optimize(input, output, options?)` - Optimize/compress image
113//!
114//! **Asset Building:**
115//! - `build_css(pattern, output, options?)` - Build and concatenate CSS files
116//!
117//! **Text Processing:**
118//! - `slugify(text)` - Convert text to URL-friendly slug
119//! - `word_count(text)` - Count words in text
120//! - `reading_time(text, wpm?)` - Calculate reading time in minutes
121//! - `truncate(text, len, suffix?)` - Truncate text with optional suffix
122//! - `strip_tags(html)` - Remove HTML tags
123//! - `format_date(date, format)` - Format a date string
124//! - `parse_date(str)` - Parse date string to table {year, month, day}
125//! - `hash(content)` - Hash content (xxHash64)
126//! - `hash_file(path)` - Hash file contents
127//! - `url_encode(str)` - URL encode a string
128//! - `url_decode(str)` - URL decode a string
129//!
130//! **Path Utilities:**
131//! - `join_path(...)` - Join path segments
132//! - `basename(path)` - Get file name from path
133//! - `dirname(path)` - Get directory from path
134//! - `extension(path)` - Get file extension
135//!
136//! **Collections:**
137//! - `filter(items, fn)` - Filter items where fn returns true
138//! - `sort(items, fn)` - Sort items using comparator
139//! - `map(items, fn)` - Transform each item
140//! - `find(items, fn)` - Find first item where fn returns true
141//! - `group_by(items, key_fn)` - Group items by key
142//! - `unique(items)` - Remove duplicates
143//! - `reverse(items)` - Reverse array order
144//! - `take(items, n)` - Take first n items
145//! - `skip(items, n)` - Skip first n items
146//! - `keys(table)` - Get all keys from a table
147//! - `values(table)` - Get all values from a table
148//!
149//! **Environment:**
150//! - `env(name)` - Get environment variable
151//! - `print(...)` - Log output to build log
152//!
153//! **Async I/O (rs.async):**
154//! - `fetch(url, options?)` - HTTP fetch (blocking)
155//! - `fetch_json(url, options?)` - Fetch and parse JSON
156//! - `fetch_all(requests)` - Fetch multiple URLs concurrently
157//! - `spawn(url, options?)` - Spawn async fetch task
158//! - `await(task)` - Await spawned task
159//! - `await_all(tasks)` - Await multiple tasks
160//! - `read(path)` - Read file as binary
161//! - `read_file(path)` - Read file as text
162//! - `read_files(paths)` - Read multiple files concurrently
163//! - `write_file(path, content)` - Write file
164//! - `copy_file(src, dst)` - Copy file
165//! - `rename(src, dst)` - Rename/move file or directory
166//! - `remove_file(path)` - Remove file
167//! - `remove_dir(path)` - Remove directory recursively
168//! - `create_dir(path)` - Create directory (including parents)
169//! - `exists(path)` - Check if path exists
170//! - `metadata(path)` - Get file metadata
171//! - `read_dir(path)` - List directory contents
172//! - `canonicalize(path)` - Get canonical/absolute path
173//!
174//! **Encryption (rs.crypt):**
175//! - `encrypt(content, password?)` - Encrypt content (AES-256-GCM)
176//! - `decrypt(data, password?)` - Decrypt content
177//! - `encrypt_html(content, options?)` - Generate encrypted HTML block for browser decryption
178//!
179//! All file operations respect the sandbox setting and are tracked for incremental builds.
180//! Encryption uses SITE_PASSWORD environment variable if password is not provided.
181//!
182//! ## Frontmatter
183//!
184//! Post frontmatter options (YAML or TOML):
185//!
186//! ```yaml
187//! ---
188//! title: "Post Title"           # Required
189//! description: "Description"    # Optional
190//! date: 2024-01-15              # Optional (YAML date or string)
191//! tags: ["tag1", "tag2"]        # Optional
192//! draft: false                  # Optional (default: false, excluded from build)
193//! image: "/static/post.png"     # Optional: OG image
194//! template: "custom.html"       # Optional: Override template
195//! slug: "custom-slug"           # Optional: Override URL slug
196//! permalink: "/custom/url/"     # Optional: Full URL override
197//! ---
198//! ```
199//!
200//! ## Encryption (via Lua)
201//!
202//! Encryption is handled via the `rs.crypt` module in Lua. Use `SITE_PASSWORD`
203//! environment variable or pass password explicitly:
204//!
205//! ```lua
206//! -- Encrypt content
207//! local encrypted = rs.crypt.encrypt("secret content")
208//! -- Returns: { ciphertext, salt, nonce }
209//!
210//! -- Generate encrypted HTML block for browser decryption
211//! local html = rs.crypt.encrypt_html("secret content", {
212//!   slug = "post-slug",
213//!   block_id = "secret-1",
214//! })
215//!
216//! -- Decrypt content
217//! local plaintext = rs.crypt.decrypt(encrypted)
218//! ```
219//!
220//! ## Template Variables
221//!
222//! ### Home Template (`home.html`)
223//! - `site` - Site config (title, description, base_url, author)
224//! - `page` - Page info (title, description, url, image)
225//! - `sections` - All sections with posts (`sections.blog.posts`)
226//! - `content` - Rendered markdown content
227//!
228//! ### Post Template (`post.html`)
229//! - `site` - Site config
230//! - `post` - Post info (title, url, date, tags, reading_time, etc.)
231//! - `page` - Page info for head.html compatibility
232//! - `content` - Rendered markdown content
233//! - `backlinks` - Posts linking to this post (url, title, section)
234//! - `graph` - Local graph data (nodes, edges) for visualization
235//!
236//! ### Graph Template (`graph.html`)
237//! - `site` - Site config
238//! - `page` - Page info
239//! - `graph` - Full graph data (nodes, edges)
240//!
241//! ## Modules
242//!
243//! - [`config`] - Configuration loading and structures
244//! - [`markdown`] - Markdown processing pipeline
245//! - [`templates`] - Tera template rendering
246//! - [`encryption`] - AES-256-GCM encryption utilities (used by `rs.crypt` Lua module)
247//! - [`build`] - Main build orchestrator
248
249pub mod assets;
250pub mod build;
251pub mod config;
252pub mod data;
253pub mod encryption;
254pub mod git;
255pub mod lua;
256pub mod markdown;
257pub mod server;
258pub mod templates;
259pub mod text;
260pub mod watch;