Skip to main content

rto_render/
lib.rs

1//! Renderers over the Roteiro graph. All outputs — docs site, OKF bundle,
2//! and the optional MCP server (feature `mcp`) — are build products of the
3//! same store, so humans and agents always see the same data.
4
5mod docs;
6/// The OKF bundle renderer (#663) — the open-format successor to the vault.
7pub mod okf;
8/// The one place a tool's class is written, and the report that stands in for a
9/// class an operator did not load (#664).
10pub mod tool_class;
11/// The one place each shared tool description is written (#590).
12pub mod tool_text;
13
14#[cfg(feature = "mcp")]
15pub mod mcp;
16
17pub use docs::{
18    IndexEntry, NavEntry, PublishedPages, RenderedAdr, SourceBase, markdown_to_html, render_adr,
19    render_adr_index, render_doc, render_doc_at, render_nav, render_site_page, replace_site_nav,
20};
21
22/// A render target for the graph.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Target {
25    /// Static documentation website (ADRs, blueprints, AI context pages).
26    DocsSite,
27    /// An Open Knowledge Format bundle (OKF v0.2).
28    ///
29    /// Replaced `ObsidianVault` in 4.0.0. The vault was one-way — Roteiro wrote
30    /// it, nothing read it back, and only Obsidian could consume it. An OKF
31    /// bundle is markdown with YAML frontmatter in nested directories, so
32    /// Obsidian still opens it as a vault; what changed is that the output now
33    /// targets a specification with other consumers rather than one
34    /// application's conventions.
35    OkfBundle,
36}
37
38impl Target {
39    /// Stable CLI name for this target.
40    #[must_use]
41    pub fn as_str(self) -> &'static str {
42        match self {
43            Self::DocsSite => "docs",
44            Self::OkfBundle => "okf",
45        }
46    }
47
48    /// Parse a target from its CLI name.
49    #[must_use]
50    pub fn parse(s: &str) -> Option<Self> {
51        match s {
52            "docs" => Some(Self::DocsSite),
53            "okf" => Some(Self::OkfBundle),
54            _ => None,
55        }
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::Target;
62
63    #[test]
64    fn target_names_are_stable() {
65        assert_eq!(Target::DocsSite.as_str(), "docs");
66        assert_eq!(Target::OkfBundle.as_str(), "okf");
67        assert_eq!(Target::parse("docs"), Some(Target::DocsSite));
68        assert_eq!(Target::parse("okf"), Some(Target::OkfBundle));
69        // The removed target must not silently resolve to something else: a
70        // script still passing `obsidian` should be told, not quietly redirected.
71        assert_eq!(Target::parse("obsidian"), None);
72        assert_eq!(Target::parse("nope"), None);
73    }
74}