Skip to main content

aidoc_core/
platform.rs

1//! Platform overlay dispatch: given a `Config::platforms` list, add
2//! the platform-specific artifacts on top of the core `docs/aidoc/`
3//! output produced by [`crate::generate::render_all`].
4//!
5//! The core output is intentionally platform-neutral (it follows
6//! [llmstxt.org](https://llmstxt.org)). Overlays add the small extra
7//! files each external doc service expects — for example, `context7.json`
8//! at the repo root, or `.devin/wiki.json` for DeepWiki — without
9//! changing the core layout. Callers opt in per platform via
10//! `--platform <name>` on the CLI or `Config::platforms` in code.
11//!
12//! This file is intentionally a thin dispatcher; each overlay's actual
13//! emitter lives in a follow-up phase.
14
15use crate::config::Platform;
16use crate::error::Result;
17use crate::generate::{self, Artifact};
18use crate::index::IndexedWorkspace;
19
20/// Apply every overlay in `platforms` to `artifacts` in the order they
21/// appear. Overlays may append new artifacts, replace existing ones by
22/// path, or mutate existing bodies in place; the dispatch loop takes no
23/// position on how they combine, only on the order they run.
24pub fn apply_overlays(
25    workspace: &IndexedWorkspace,
26    artifacts: &mut Vec<Artifact>,
27    platforms: &[Platform],
28) -> Result<()> {
29    for platform in platforms {
30        match platform {
31            Platform::Context7 => {
32                let body = generate::render_context7_manifest(workspace);
33                artifacts.push(Artifact::in_workspace_root("context7.json", body));
34            }
35            Platform::DeepWiki => {
36                let body = generate::render_deepwiki_manifest(workspace);
37                artifacts.push(Artifact::in_workspace_root(".devin/wiki.json", body));
38            }
39            Platform::AnthropicStyle => {
40                apply_anthropic_style(artifacts);
41            }
42        }
43    }
44    Ok(())
45}
46
47/// Prepend a reverse cross-ref blockquote to every markdown artifact
48/// and to `llms-full.txt`, mirroring Anthropic's `platform.claude.com`
49/// docs layout where every `.md` mirror carries a hint back to the
50/// top-level index.
51///
52/// The link uses a filesystem-relative path (`../llms.txt` for
53/// per-crate / per-module pages, `llms.txt` for artifacts that live at
54/// the output root). A later phase can add a `--base-url` option to
55/// swap those relative paths for absolute URLs when the crate author
56/// knows the publish target.
57fn apply_anthropic_style(artifacts: &mut [Artifact]) {
58    for artifact in artifacts.iter_mut() {
59        if !is_markdown_target(&artifact.path) {
60            continue;
61        }
62        let link = if artifact.path.contains('/') {
63            "../llms.txt"
64        } else {
65            "llms.txt"
66        };
67        let hint = format!("> Fetch the complete documentation index at: {link}\n\n");
68        let mut new_body = String::with_capacity(hint.len() + artifact.body.len());
69        new_body.push_str(&hint);
70        new_body.push_str(&artifact.body);
71        artifact.body = new_body;
72    }
73}
74
75fn is_markdown_target(path: &str) -> bool {
76    path.ends_with(".md") || path == "llms-full.txt"
77}