docs: drop the predecessor comparisons from comments and tests

This commit is contained in:
randogoth 2026-10-06 10:27:46 +03:00
parent 36faa4fb93
commit 19e7cfaca3
19 changed files with 65 additions and 73 deletions

View file

@ -3,8 +3,7 @@
//! Invalidation is by modification time and length, which is what makes editing
//! a file enough to see the change on the next request with no watcher and no
//! restart. Values are built outside the lock, so two first hits on one file can
//! both build it; the work is idempotent and the second insert wins, which is
//! the trade the Python makes too.
//! both build it; the work is idempotent and the second insert wins.
use std::collections::HashMap;
use std::fs;
@ -109,9 +108,9 @@ mod tests {
use super::*;
/// Force a modification time change. Filesystem granularity is coarse
/// enough that two writes in one test can otherwise share a timestamp,
/// which is why the Python's live-reload test does the same thing.
/// Force a modification time change. Filesystem granularity is coarse enough
/// that two writes in one test can otherwise share a timestamp, which would
/// make a stale value look correctly cached.
fn bump_mtime(path: &Path) {
let later = SystemTime::now() + Duration::from_secs(5);
File::options()
@ -137,7 +136,8 @@ mod tests {
let first = cache.get_or_insert_with(&path, Stamp::of(&path).unwrap(), build).unwrap();
let second = cache.get_or_insert_with(&path, Stamp::of(&path).unwrap(), build).unwrap();
// Identity, not just equality: the Python asserts `first is second`.
// Identity, not equality: a repeat hit must return the cached value
// rather than an equal rebuild.
assert!(Arc::ptr_eq(&first, &second));
assert_eq!(builds.load(Ordering::Relaxed), 1);
}

View file

@ -670,8 +670,8 @@ impl Default for PageSettings {
// the terminal edge.
margin_left: 2,
margin_right: 2,
// One blank line between blocks. md2txt uses two, which reads as
// double-spaced throughout.
// One blank line between blocks; two reads as double-spaced
// throughout.
paragraph_spacing: 1,
heading_styles: DEFAULT_HEADING_STYLES,
blockquote_bars: true,

View file

@ -1,10 +1,9 @@
//! The one parsed representation every output format consumes.
//!
//! smolweb parses each document twice, with two hand-rolled regex parsers that
//! share seven identical patterns but disagree on the edges: that is where the
//! `{.card}` directive leaks into gemtext as literal text, and why the two
//! libraries carry two divergent sets of defaults. Parsing once into this
//! structure removes the class of bug rather than the instances.
//! One parse, shared by every format. Parsing per format is how two outputs come
//! to disagree about the same document: a directive one understands and another
//! emits as literal text, or two sets of defaults that drift apart. Parsing once
//! into this structure removes that class of bug rather than its instances.
//!
//! It is a flat block sequence rather than a tree of nodes because that is what
//! the consumers want: WML packs a linear run of blocks into byte-budgeted

View file

@ -1,9 +1,9 @@
//! Media types for files served byte for byte.
//!
//! A fixed table rather than a system lookup: Python's `mimetypes` consults
//! `/etc/mime.types` where it exists, so smolweb's `Content-Type` for the same
//! file differs between hosts. Determinism is worth more here than coverage of
//! the long tail, and an unknown extension has a correct answer anyway.
//! A fixed table rather than a system lookup. A lookup reads `/etc/mime.types`
//! where it exists, so the same file gets a different `Content-Type` depending on
//! the host. Determinism is worth more here than coverage of the long tail, and
//! an unknown extension has a correct answer anyway.
use std::path::Path;

View file

@ -427,8 +427,6 @@ mod tests {
#[test]
fn parses_setext_headings() {
// wapdown's parser handles these and md2txt's does not, so unifying the
// two parsers gains them for the text formats.
assert_eq!(
blocks("Title\n=====\n"),
vec![Block::Heading { level: 1, inline: text("Title") }]

View file

@ -82,8 +82,8 @@ fn encode_path(path: &str) -> String {
/// Decode `%XX` escapes, leaving an invalid escape as the literal text it is.
///
/// Bytes that do not form valid UTF-8 become U+FFFD, which matches no filename,
/// so a malformed target resolves to nothing rather than erroring. That matches
/// Python's lossy `unquote` and is pinned by a test.
/// so a malformed target resolves to nothing rather than erroring. A test pins
/// that leniency, so it is not later tightened into an error.
fn percent_decode(raw: &str) -> String {
let bytes = raw.as_bytes();
let mut out = Vec::with_capacity(bytes.len());
@ -145,9 +145,8 @@ mod tests {
#[test]
fn clamps_traversal_at_the_root() {
// Ported from smolweb's TestPathTraversal: these must never reach above
// the root, and since nothing is mounted at the clamped path they
// resolve to a path that simply does not exist.
// These must never reach above the root, and since nothing is mounted at
// the clamped path they resolve to a path that simply does not exist.
assert_eq!(clean("/../../etc/passwd"), "etc/passwd");
assert_eq!(clean("/../../../../../../etc/passwd"), "etc/passwd");
assert_eq!(clean("/foo/../../etc/passwd"), "etc/passwd");
@ -159,7 +158,7 @@ mod tests {
// before the clamp runs, or it would be treated as a literal segment.
assert_eq!(clean("/%2e%2e/etc/passwd"), "etc/passwd");
assert_eq!(clean("/%2E%2E/etc/passwd"), "etc/passwd");
// An encoded separator becomes a separator, as Python's unquote does.
// An encoded separator becomes a real one, so normalising then sees it.
assert_eq!(clean("/dir%2fpage"), "dir/page");
assert_eq!(clean("/hello%20world"), "hello world");
}

View file

@ -7,10 +7,10 @@
//! [`crate::directives`].
//!
//! Together with that module this is the only part of the pipeline that opens
//! files, which gives the root-containment check exactly one home. That closes
//! the traversal smolweb has: md2txt resolves an include target and checks only
//! that it exists, so `{.include ../../../../etc/passwd}` in any served document
//! reads and emits that file.
//! files, which gives the root-containment check exactly one home. An include
//! target is canonicalised and required to be inside the root, so a target of
//! `../../../../etc/passwd` resolves to nothing instead of being read: checking
//! only that a target exists is what makes includes a traversal.
use std::collections::BTreeSet;
use std::fs;
@ -24,7 +24,7 @@ use crate::error::{Error, IncludeReason};
const MAX_DEPTH: usize = 16;
/// Caps on the expanded result. The cycle set is per-*stack*, so a diamond —
/// `a` includes `b` and `c`, both include `d` — fans out exponentially without
/// ever repeating a file on one path. smolweb has nothing that stops this.
/// ever repeating a file on one path, so only these caps stop it.
const MAX_LINES: usize = 200_000;
const MAX_BYTES: usize = 8 * 1024 * 1024;
@ -159,7 +159,7 @@ mod tests {
assert_eq!(include_target("see ![[notes.md]] there"), None);
assert_eq!(include_target("![[]]"), None);
assert_eq!(include_target("![[unterminated"), None);
// The directive smolweb also accepted is gone: one spelling, not two.
// The brace-directive form is not accepted: one spelling, not two.
assert_eq!(include_target("{.include notes.md}"), None);
}
@ -263,8 +263,8 @@ mod tests {
#[test]
fn a_diamond_fan_out_is_stopped_by_the_size_cap() {
// Each level doubles and no file repeats on any single path, so neither
// the cycle set nor the depth cap catches it. smolweb expands this until
// it runs out of memory.
// the cycle set nor the depth cap catches it, which leaves the byte cap
// as the only thing that stops it.
let tree = Tree::new();
tree.write("leaf.md", &"filler line\n".repeat(64));
let mut previous = "leaf.md".to_string();

View file

@ -3,8 +3,8 @@
//! A format is a crate implementing [`Renderer`], registered at startup behind a
//! cargo feature. The trait takes a parsed [`Doc`] rather than source text so
//! that every format reads one parse: gemtext's `=>` link catalogue and the text
//! formats' `[n]` references must agree about link identity and order, and in
//! smolweb they can disagree because each library re-parses.
//! formats' `[n]` references must agree about link identity and order, which
//! cannot be relied on when each format parses the source for itself.
//!
//! A renderer must not open files or sockets. Includes and art are already
//! resolved by the time it runs, which is what keeps the root-containment check

View file

@ -267,8 +267,8 @@ mod tests {
Site::new(root, registry(), vec!["stub".to_string()]).unwrap()
}
/// Mirrors smolweb's `tests/conftest.py` fixture, so its assertions port
/// across directly.
/// One of each kind of thing a request can land on, shared by the resolution
/// tests below.
fn fixture() -> (tempfile::TempDir, Site) {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
@ -449,9 +449,9 @@ mod tests {
#[test]
fn a_bare_unresolvable_segment_resolves_to_nothing() {
// Regression carried over from smolweb: the Python reached this path
// through `rpartition("/")`, where a bare top-level segment yields an
// empty parent that must not be read as the root index.
// The obvious implementation splits on the last `/`, where a bare
// top-level segment yields an empty parent that must not then be read as
// the root index.
let (_dir, site) = fixture();
assert_not_found(&site, "/totally-unresolvable-segment");
}
@ -568,9 +568,9 @@ mod tests {
#[cfg(test)]
mod part_tests {
//! Ported from smolweb's `TestWmlCardUrls`. A stub renderer stands in for a
//! paginating format, so these rules are tested without the WML crate: core
//! does not know which formats paginate, which is the point.
//! A stub renderer stands in for a paginating format, so these rules are
//! tested without the WML crate: core does not know which formats paginate,
//! which is the point.
use std::fs;