Skip to main content

engine_observables_api/
semantic.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Common semantic facts every lane that has a document publishes —
6//! plus engine-specific extensions for richer protocol-native shape.
7//!
8//! Hekate's indexing / search / preview pipeline consumes
9//! [`SemanticQuery`] only — uniform across lanes. Apparatus's
10//! inspector pane can downcast to a lane-specific extension when
11//! present (e.g., `if let Some(html) = doc.as_html_ext()`).
12//!
13//! Cf. Hekate doc §"Semantic Plane". Per Mark's correction: don't
14//! force one fake tree model across lanes — use common-minimum +
15//! engine-specific extensions instead.
16
17use malloc_size_of_derive::MallocSizeOf;
18use serde::{Deserialize, Serialize};
19
20use crate::types::{Lang, SourceNodeId, SourceRange};
21
22/// Common-minimum semantic queries every lane with a document
23/// publishes. The trait is generic over `NodeId` so consumers that
24/// hold a concrete impl can index without boxing; engine-agnostic
25/// dispatch (Hekate) reads `SourceNodeId` via the inferred
26/// `as_source_id` helper each impl defines.
27pub trait SemanticQuery {
28    type NodeId: Copy + Eq + std::hash::Hash;
29
30    /// Document title (HTML `<title>`, Atom `<title>`, RSS channel
31    /// title, Gemini frontmatter title, Scroll frontmatter, etc.).
32    fn title(&self) -> Option<&str>;
33
34    /// Document language (HTML `lang` attribute, Atom `xml:lang`,
35    /// etc.). BCP 47 tag.
36    fn language(&self) -> Option<&Lang>;
37
38    /// Heading hierarchy. Lane impls pick their own ordering — most
39    /// will iterate in document order.
40    fn headings<'a>(&'a self) -> Box<dyn Iterator<Item = HeadingInfo> + 'a>;
41
42    /// Outbound links.
43    fn links<'a>(&'a self) -> Box<dyn Iterator<Item = LinkInfo> + 'a>;
44
45    /// Named anchors (`<a name>`, `#fragment`-target headings, etc.).
46    fn anchors<'a>(&'a self) -> Box<dyn Iterator<Item = AnchorInfo> + 'a>;
47
48    /// Nodes matching a generic semantic role (e.g., `Main`,
49    /// `Navigation`). Useful for reader-mode extraction.
50    fn nodes_by_role<'a>(
51        &'a self,
52        role: SemanticRole,
53    ) -> Box<dyn Iterator<Item = Self::NodeId> + 'a>;
54
55    /// Text content of a node, if it has any (text leaf, headings,
56    /// link text).
57    fn text_range(&self, node: Self::NodeId) -> Option<&str>;
58
59    /// Source-byte range a node corresponds to. Lanes that don't
60    /// track source spans return `None`.
61    fn source_range(&self, node: Self::NodeId) -> Option<SourceRange>;
62}
63
64/// One heading and its level.
65#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
66pub struct HeadingInfo {
67    pub source_node: SourceNodeId,
68    /// 1..=6 for HTML, lane-specific for others (Markdown allows
69    /// 1..=6 too; Scroll's headings flatten; Gemini has one level).
70    pub level: u8,
71    pub text: String,
72}
73
74/// One outbound link.
75#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
76pub struct LinkInfo {
77    pub source_node: SourceNodeId,
78    pub href: String,
79    /// Visible link text (may be empty for image links).
80    pub text: String,
81}
82
83/// One named anchor (target of `#fragment` navigation).
84#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
85pub struct AnchorInfo {
86    pub source_node: SourceNodeId,
87    pub name: String,
88}
89
90/// Generic semantic role consumers can ask about, independent of any
91/// specific markup language. Map onto each lane's native vocabulary.
92#[derive(
93    Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize,
94)]
95pub enum SemanticRole {
96    Main,
97    Navigation,
98    Article,
99    Section,
100    Aside,
101    Header,
102    Footer,
103    Heading,
104    Paragraph,
105    List,
106    ListItem,
107    Quote,
108    Code,
109    Form,
110    #[default]
111    Generic,
112}