module

Hwaro::Services::ContentWalk

Filesystem guard shared by the content-walking tool services (tool list / tool stats here, plus tool convert, tool validate and tool unused-assets). It lives beside the lister because that is the walker the other services already build on.

Dir.glob reports a symlink as an ordinary path, so a self-referential link (ln -s loop.md content/loop.md), a mutually-pointing pair, or a dangling link all come back looking like a normal file. Resolving one fails with ELOOP/ENOENT, and File.info? — hence File.directory? — RAISES File::Error on ELOOP, because it only swallows ENOENT/ENOTDIR. hwaro build adopted an lstat-first guard for exactly this tree (see core/build/phases/read_content.cr), but the tool commands still walked it blind: tool unused-assets died with a raw filesystem error, and the others turned every bad link into a read failure they reported as the author's problem. Skip such entries the way the build does.

Unlike the build this guard judges READABILITY only, never containment: the build refuses a link resolving outside the project because it would publish the target, whereas these commands merely read, and they take an arbitrary --content-dir that need not sit under Dir.current — testing containment against that root would skip every file the user asked about.

Constants

MARKDOWN_EXTENSIONS = {".md", ".markdown"}

Instance methods

find_content_files(content_dir : String, ignore : Ignore = ignore_rules(content_dir)) : Array(String)

True when path is a regular file that can actually be opened.

Anything that simply is not a file (a directory, or a FIFO/socket whose File.read would block forever) is skipped without comment — it was never content. A symlink we cannot follow is named in a single warning instead, matching Phases::Initialize#static_target_info, so a file missing from a listing or a validate summary is never silent. Every Markdown file under content_dir, sorted. Unfollowable symlinks are dropped here (see readable_file?) so one bad link cannot turn a listing, a validate summary or a convert run into a wall of "Failed to read content file" warnings — or a non-zero exit — for files the build itself skips. tool list, tool stats, tool validate and tool convert all walk through here, so they stay consistent with each other.

Files [content] ignore hides are left out, so no tool lists, validates, converts or link-checks what the build never reads.

Source
ignore_rules(content_dir : String, config : Models::Config, config_path : String) : Ignore

The rules of an already loaded config (read from config_path).

Source
ignore_rules(content_dir : String) : Ignore

The [content] ignore rules of content_dir's project (see project_config_path) for a walk of content_dir; none when there is no loadable config. ponytail: base config only — like every other tool without --env, an overlay's ignore list is not read; doctor passes its own.

Source
markdown?(path : String) : Bool

True when path carries a Markdown page extension, compared case-insensitively like ReadContent's PAGE_EXTENSIONS check.

Source
project_config(content_dir : String) : Models::Config | Nil

The project config a content-walking tool reads (see project_config_path). Loaded quietly — the build owns config diagnostics, here they would only repeat. Nil when there is none or it won't load.

Source
project_config_path(content_dir : String) : String | Nil

Where the project config of content_dir lives: config.toml beside it, else the working directory's (ContentLister's language lookup). Nil when there is none.

Source
readable_file?(path : String) : Bool
Source

Nested types