Skip to main content

moss_core/
page_kind.rs

1//! Classification of moss page/source-file kinds.
2//!
3//! moss recognizes exactly three kinds of page-producing entities in a
4//! source tree. This enum is the canonical way to tell them apart — do
5//! NOT use `is_index`, filename checks, or extension sniffing at call
6//! sites. The kind is set once at ingestion (for markdown files) or
7//! synthesis (for asset pages) and read everywhere else.
8//!
9//! # Variants
10//!
11//! * [`PageKind::Article`] — a markdown file that is not a folder index.
12//!   Authored prose content. Listed in children at any depth.
13//! * [`PageKind::Folder`] — a markdown file recognized by
14//!   [`crate::home::is_home_file`]. Represents the directory it lives in
15//!   (e.g. `文字/文字.md`, `docs/index.md`, `blog/readme.md`). Listed in
16//!   children only at depth 1 (at depth `all`, its descendants are listed
17//!   directly).
18//!
19//! # Why an enum, not a bool
20//!
21//! The previous model used `is_index: bool` and inferred the rest from
22//! context. That made the children-listing filter impossible to get right.
23//! A third `Asset` variant (synthetic per-image pages) existed until the
24//! image-as-page feature was removed in 2026-07; nothing produced it, so it
25//! was dropped rather than left as an unreachable state. See
26//! `moss/docs/reference/page-kinds.md`.
27
28use serde::{Deserialize, Serialize};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(rename_all = "snake_case")]
32pub enum PageKind {
33    /// Markdown file, not a folder index.
34    Article,
35    /// Markdown file recognized as a folder index by
36    /// [`crate::home::is_home_file`].
37    Folder,
38}
39
40impl PageKind {
41    /// True when pages of this kind appear in children listings at
42    /// `children_depth: all`.
43    pub fn is_listable_at_depth_all(self) -> bool {
44        matches!(self, PageKind::Article)
45    }
46
47    /// True when pages of this kind appear in children listings at
48    /// `children_depth: direct`.
49    pub fn is_listable_at_depth_direct(self) -> bool {
50        matches!(self, PageKind::Article | PageKind::Folder)
51    }
52}
53
54impl Default for PageKind {
55    fn default() -> Self {
56        PageKind::Article
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn listable_at_depth_all_is_article_only() {
66        assert!(PageKind::Article.is_listable_at_depth_all());
67        assert!(!PageKind::Folder.is_listable_at_depth_all());
68    }
69
70    #[test]
71    fn listable_at_depth_direct_is_article_and_folder() {
72        assert!(PageKind::Article.is_listable_at_depth_direct());
73        assert!(PageKind::Folder.is_listable_at_depth_direct());
74    }
75
76    #[test]
77    fn default_is_article() {
78        assert_eq!(PageKind::default(), PageKind::Article);
79    }
80}