Skip to main content

aidoc_core/
config.rs

1//! Configuration for the aidoc pipeline.
2//!
3//! The `Config` merges three sources with a fixed precedence:
4//!
5//! 1. CLI arguments (highest precedence, wins over anything else)
6//! 2. `[package.metadata.aidoc]` in a member crate's `Cargo.toml`
7//! 3. `[workspace.metadata.aidoc]` in the workspace `Cargo.toml`
8//!
9//! Values not set by any source fall back to the defaults documented on each
10//! field. `Config` itself is a plain struct; the merge logic lives in the
11//! index stage where the workspace layout is already known.
12
13use std::path::PathBuf;
14
15/// Runtime configuration for a single aidoc invocation.
16#[derive(Debug, Clone)]
17pub struct Config {
18    /// Output directory for generated artifacts. Defaults to
19    /// `<repo>/docs/aidoc/` relative to the workspace root.
20    pub out_dir: PathBuf,
21
22    /// Which artifact set to emit. Currently only [`Preset::Publish`].
23    pub preset: Preset,
24
25    /// If true, promote lint warnings to errors and cause the pipeline to
26    /// exit with a non-zero code. Wired to `--strict` on the CLI.
27    pub strict: bool,
28
29    /// If true, run in check mode: generate artifacts into a temporary
30    /// directory and diff against the committed `out_dir`. Wired to
31    /// `--check` on the CLI. No files are written to `out_dir`.
32    pub check: bool,
33
34    /// Crate names to skip when enumerating the workspace. Sourced from
35    /// `[workspace.metadata.aidoc].exclude`.
36    pub exclude: Vec<String>,
37
38    /// External LLM-doc platforms to emit overlay artifacts for.
39    ///
40    /// Each entry adds platform-specific files on top of the core
41    /// `docs/aidoc/` output (e.g. `context7.json` at the repo root for
42    /// [`Platform::Context7`]). See the `platform` module for what each
43    /// overlay emits.
44    pub platforms: Vec<Platform>,
45}
46
47impl Default for Config {
48    fn default() -> Self {
49        Self {
50            out_dir: PathBuf::from("docs/aidoc"),
51            preset: Preset::Publish,
52            strict: false,
53            check: false,
54            exclude: Vec::new(),
55            platforms: Vec::new(),
56        }
57    }
58}
59
60/// Which artifact set the pipeline should emit.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum Preset {
63    /// Emit the full artifact set intended for publishing:
64    /// `llms.txt`, per-crate narrative markdown, `llms-full.txt`, and
65    /// deterministic `api/<crate>.json`.
66    Publish,
67}
68
69/// External LLM-doc platform this crate can emit overlay artifacts for.
70///
71/// Each variant maps to a small set of extra files (typically a
72/// platform manifest at the repo root) that a downstream service
73/// consumes. Layouts and required fields are captured in the
74/// `platform` module.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum Platform {
77    /// [Context7](https://context7.com): manual-submit + GitHub Action
78    /// service. Overlay emits `context7.json` at the repo root
79    /// (`projectTitle`, `description`, `folders`, `branch`).
80    Context7,
81    /// [DeepWiki](https://deepwiki.com): auto-crawl service. Overlay
82    /// emits `.devin/wiki.json` at the repo root for workspaces large
83    /// enough to hit DeepWiki's page-count limit.
84    DeepWiki,
85    /// Anthropic-style: `.md` page mirror + reverse cross-ref hint
86    /// pointing back at the workspace `llms.txt`. Modifies existing
87    /// per-module `.md` artifacts in place; adds no new files.
88    AnthropicStyle,
89}
90
91impl Platform {
92    /// The name expected by the CLI (`--platform <name>`) and by any
93    /// future configuration parser.
94    pub fn as_str(self) -> &'static str {
95        match self {
96            Self::Context7 => "context7",
97            Self::DeepWiki => "deepwiki",
98            Self::AnthropicStyle => "anthropic-style",
99        }
100    }
101}
102
103impl std::str::FromStr for Platform {
104    type Err = UnknownPlatform;
105    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
106        match s {
107            "context7" => Ok(Self::Context7),
108            "deepwiki" => Ok(Self::DeepWiki),
109            "anthropic-style" => Ok(Self::AnthropicStyle),
110            other => Err(UnknownPlatform(other.to_owned())),
111        }
112    }
113}
114
115/// Returned by [`Platform::from_str`] when the input doesn't match any
116/// known platform name.
117#[derive(Debug, Clone)]
118pub struct UnknownPlatform(pub String);
119
120impl std::fmt::Display for UnknownPlatform {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(
123            f,
124            "unknown platform `{}`; expected one of: context7, deepwiki, anthropic-style",
125            self.0
126        )
127    }
128}
129
130impl std::error::Error for UnknownPlatform {}