Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,19 @@ dead_code = "deny"
rust_2024_compatibility = { level = "warn", priority = -1 }

[workspace.lints.rustdoc]
# Docs are a shipped surface: broken intra-doc links are a defect, not a
# smell. Keep this one `deny`.
# Docs are a shipped surface. All rustdoc lints are `deny` (matching the
# sibling aozora repo): broken / private links, bad codeblocks, invalid
# HTML, bare URLs, redundant links, and unescaped backticks are defects,
# not smells. A genuinely work-in-progress doc surface takes a scoped
# `#[allow(rustdoc::…, reason = "…")]`, never a blanket warn.
broken_intra_doc_links = "deny"
# Everything else warns so drift shows up in CI without breaking the build
# while docs get fleshed out.
private_intra_doc_links = "warn"
invalid_codeblock_attributes = "warn"
invalid_html_tags = "warn"
invalid_rust_codeblocks = "warn"
bare_urls = "warn"
redundant_explicit_links = "warn"
unescaped_backticks = "warn"
private_intra_doc_links = "deny"
invalid_codeblock_attributes = "deny"
invalid_html_tags = "deny"
invalid_rust_codeblocks = "deny"
bare_urls = "deny"
redundant_explicit_links = "deny"
unescaped_backticks = "deny"

[workspace.lints.clippy]
# Opt-in to all three lint groups that mainstream Rust libraries ship with.
Expand Down Expand Up @@ -219,9 +220,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
clap = { version = "4.6.1", features = ["derive", "wrap_help", "env", "color", "cargo"] }

# Testing
insta = { version = "1.47", features = ["yaml", "json", "filters"] }
proptest = "1.11"
criterion = { version = "0.8", features = ["html_reports"] }
pretty_assertions = "1.4.1"

[profile.release]
Expand Down
23 changes: 11 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,18 @@ COPY --from=cargo-tools /usr/local/bin/ /usr/local/bin/
# means the sync never fires.
RUN rustup component add rustfmt clippy rust-src

ENV CARGO_HOME=/workspace/.cargo \
CARGO_TARGET_DIR=/workspace/target \
ENV CARGO_HOME=/cargo/home \
CARGO_TARGET_DIR=/cargo/target \
RUSTC_WRAPPER=sccache \
SCCACHE_DIR=/workspace/.sccache \
SCCACHE_DIR=/cargo/sccache \
RUST_BACKTRACE=1

# Pre-create cache mount targets so the runtime volume mounts at
# /workspace/{target,.cargo,.sccache} can attach without docker
# needing to mkdirat() into a read-only `/workspace`. Without these
# the `:ro` bind mount of the source tree blocks volume attachment
# and `docker compose run --rm ci ...` fails at container start with
# "read-only file system" during mountpoint creation.
RUN mkdir -p /workspace/target /workspace/.cargo /workspace/.sccache
# Pre-create the cache mount targets at /cargo/* so the named volume
# mounts attach cleanly. These live OUTSIDE the /workspace bind mount
# on purpose (see docker-compose.yml): nesting them under /workspace
# made the daemon create root-owned ./target / ./.cargo / ./.sccache
# on the host, littering the working tree and breaking host-side cargo.
RUN mkdir -p /cargo/target /cargo/home/registry /cargo/home/git /cargo/sccache

WORKDIR /workspace

Expand All @@ -192,9 +191,9 @@ FROM dev AS fuzz

# `rustup toolchain install` tries to self-update by looking for the
# rustup binary at $CARGO_HOME/bin/rustup. The inherited
# `CARGO_HOME=/workspace/.cargo` (set in the dev stage for runtime
# `CARGO_HOME=/cargo/home` (set in the dev stage for runtime
# volume mounts) is empty at image-build time, so the self-update step
# bails with "rustup is not installed at '/workspace/.cargo'". Override
# bails with "rustup is not installed at '/cargo/home'". Override
# the env for this one RUN so rustup finds itself at the parent rust
# image's `/usr/local/cargo` location; the runtime CARGO_HOME setting
# is unaffected.
Expand Down
20 changes: 10 additions & 10 deletions crates/afm-markdown/src/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
//! `Gaiji` / `Annotation` (inline) and `Container` / `PageBreak` /
//! `SectionBreak` (block). Heading hints
//! (`[#「X」は大見出し]`) promote their host paragraph to
//! `IrBlock::Heading` directly, mirroring [`crate::ast_splice`].
//! `IrBlock::Heading` directly, mirroring `crate::ast_splice`.
//!
//! # Module map
//!
//! - [`types`] — public IR enum/struct definitions (`IrDocument`,
//! - `types` — public IR enum/struct definitions (`IrDocument`,
//! `IrBlock`, `IrInline`, `Range`, ...).
//! - [`projection`] — pure helpers that convert `AozoraNode`
//! - `projection` — pure helpers that convert `AozoraNode`
//! variants into IR values plus the enum→string mappers and the
//! sourcepos→range bridge. No walker state.
//! - This file (`mod.rs`) — the stateful walker (`IrWalker`,
Expand All @@ -35,17 +35,17 @@
//!
//! The walker is built from three small primitives:
//!
//! 1. [`crate::sentinel_stream::SentinelCursor`] — the shared registry-stream
//! cursor. The HTML splicer ([`crate::ast_splice`]) and this
//! 1. `crate::sentinel_stream::SentinelCursor` — the shared registry-stream
//! cursor. The HTML splicer (`crate::ast_splice`) and this
//! builder both consume the same source-order sequence of
//! `NodeRef` entries; the cursor abstraction keeps them in
//! lockstep.
//! 2. [`ParaScan`] — single-descent paragraph profile. One walk per
//! 2. `ParaScan` — single-descent paragraph profile. One walk per
//! paragraph computes both the sole-block-sentinel test and the
//! heading-hint lookahead at once, eliminating the two-scan
//! redundancy that a naive translation of the HTML splicer would
//! have.
//! 3. [`OpenContainer`] — the per-walker container stack. Where the
//! 3. `OpenContainer` — the per-walker container stack. Where the
//! HTML splicer can stream open/close tags into a string buffer,
//! the IR demands a tree, so each open container collects
//! `IrBlock`s into its own `Vec` until the matching close arrives.
Expand Down Expand Up @@ -131,7 +131,7 @@ impl<'src> StreamingIrBuilder<'src> {

/// Walk a single comrak block, advancing the shared cursor.
/// Streaming-mode containers fragment per-block; for whole-doc
/// nesting use [`build_ir`].
/// nesting use `build_ir`.
pub fn walk_block<'a>(&mut self, node: &'a AstNode<'a>) -> Vec<IrBlock> {
// Move the cursor into a freshly-constructed walker for the
// duration of this call, then take it back. The walker's
Expand All @@ -154,7 +154,7 @@ impl<'src> StreamingIrBuilder<'src> {
/// Tree builder that consumes comrak nodes plus a sentinel cursor and
/// emits `IrBlock`s into a stack-balanced container hierarchy.
///
/// The state mirrors [`crate::ast_splice`]'s splicer for the HTML
/// The state mirrors `crate::ast_splice`'s splicer for the HTML
/// side: same cursor, same balanced-container model, same
/// orphan-close drain at end-of-document. They differ only in the
/// emit target (rewritten comrak AST vs. tree of `Vec<IrBlock>`).
Expand Down Expand Up @@ -195,7 +195,7 @@ impl<'src> IrWalker<'src> {

/// Drain any unclosed containers (mirror of the HTML splicer's
/// end-of-document orphan-close pass) and return the document
/// blocks. Used by [`build_ir`].
/// blocks. Used by `build_ir`.
fn finish(self) -> Vec<IrBlock> {
self.finish_keeping_cursor().0
}
Expand Down
62 changes: 34 additions & 28 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@
name: afm

x-common-env: &common-env
CARGO_HOME: /workspace/.cargo
CARGO_TARGET_DIR: /workspace/target
# Cargo caches live at /cargo/* — OUTSIDE the /workspace bind mount — on
# purpose. Nesting a named volume inside the bind mount (the old
# /workspace/target etc.) makes the Docker daemon create the host-side
# mountpoint (./target, ./.cargo, ./.sccache) as **root**, which litters
# the working tree with root-owned junk and breaks any host-side cargo.
# Keeping the caches at /cargo/* leaves the host's own ./target free and
# keeps the checkout clean. The named volumes still persist them.
CARGO_HOME: /cargo/home
CARGO_TARGET_DIR: /cargo/target
RUSTC_WRAPPER: sccache
SCCACHE_DIR: /workspace/.sccache
SCCACHE_DIR: /cargo/sccache
# sccache cannot cache crates compiled with cargo's incremental mode.
# Cargo defaults incremental=true for workspace members in dev/test profiles,
# which silently bypasses sccache for the very crates we build most often.
Expand Down Expand Up @@ -55,10 +62,10 @@ services:
environment: *common-env
volumes:
- .:/workspace:cached
- cargo-registry:/workspace/.cargo/registry
- cargo-git:/workspace/.cargo/git
- cargo-target:/workspace/target
- sccache:/workspace/.sccache
- cargo-registry:/cargo/home/registry
- cargo-git:/cargo/home/git
- cargo-target:/cargo/target
- sccache:/cargo/sccache
init: true
tty: true
stdin_open: true
Expand All @@ -76,10 +83,10 @@ services:
environment: *common-env
volumes:
- .:/workspace:cached
- cargo-registry:/workspace/.cargo/registry
- cargo-git:/workspace/.cargo/git
- cargo-target:/workspace/target
- sccache:/workspace/.sccache
- cargo-registry:/cargo/home/registry
- cargo-git:/cargo/home/git
- cargo-target:/cargo/target
- sccache:/cargo/sccache
init: true
tty: true
stdin_open: true
Expand All @@ -95,17 +102,16 @@ services:
<<: *common-env
CI: "true"
volumes:
# `:cached` (not `:ro`): a `:ro` bind mount HIDES `/workspace/{target,
# .cargo,.sccache}` that were `mkdir -p`-ed in the image (overlayfs
# bind-mount semantics — the source dir replaces image contents),
# which then blocks the named-volume mountpoints below from
# attaching at runtime ("mkdirat ... read-only file system"). The
# runner is ephemeral; `:cached` is safe and the canonical CI mode.
# `:cached` (writable): CI steps emit artifacts back into the tree
# (wasm `pkg/`, generated docs, coverage reports), so the source
# mount stays writable. The cargo caches now live at /cargo/*
# (outside this bind mount), so the old ":ro hides the nested
# /workspace/{target,.cargo,.sccache} mountpoints" hazard is gone.
- .:/workspace:cached
- cargo-registry:/workspace/.cargo/registry
- cargo-git:/workspace/.cargo/git
- cargo-target:/workspace/target
- sccache:/workspace/.sccache
- cargo-registry:/cargo/home/registry
- cargo-git:/cargo/home/git
- cargo-target:/cargo/target
- sccache:/cargo/sccache

book:
build:
Expand All @@ -131,9 +137,9 @@ services:
CI: "true"
volumes:
- .:/workspace:cached
- cargo-registry:/workspace/.cargo/registry
- cargo-git:/workspace/.cargo/git
- cargo-target:/workspace/target
- cargo-registry:/cargo/home/registry
- cargo-git:/cargo/home/git
- cargo-target:/cargo/target

# Vite dev / preview server for the browser playground.
# Reuses the `dev` image (which already has Node 22 + wasm-pack) so no
Expand All @@ -150,10 +156,10 @@ services:
environment: *common-env
volumes:
- .:/workspace:cached
- cargo-registry:/workspace/.cargo/registry
- cargo-git:/workspace/.cargo/git
- cargo-target:/workspace/target
- sccache:/workspace/.sccache
- cargo-registry:/cargo/home/registry
- cargo-git:/cargo/home/git
- cargo-target:/cargo/target
- sccache:/cargo/sccache
- playground-node-modules:/workspace/playground/node_modules
ports:
- "5173:5173"
Expand Down
Loading