Skip to main content

code_split_plugin_api/
plugin.rs

1//! The [`LanguagePlugin`] trait + [`Options`] + [`Preset`].
2//!
3//! A plugin is a **pure parser**: it turns a workspace into nodes + edges at
4//! a requested level. It computes **no metrics** — complexity, cycles,
5//! Henry-Kafura and stats are filled centrally by the orchestrator, for all
6//! languages. The CLI holds the registry of plugins; it talks to them ONLY
7//! through this trait and never names a concrete language.
8
9use crate::graph::Graph;
10use crate::level::{Level, Thresholds};
11use anyhow::Result;
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14use std::path::Path;
15
16/// Free-form key/value options passed from the CLI (future `--plugin-opt k=v`).
17/// `BTreeMap` for deterministic iteration order.
18pub type Options = BTreeMap<String, String>;
19
20/// Everything the orchestrator feeds a plugin from config + CLI input.
21#[derive(Debug, Clone, Default)]
22pub struct PluginInput {
23    /// Glob patterns for paths to skip during analysis (config + CLI).
24    pub ignore: Vec<String>,
25    /// When `true`, the plugin must skip its own **test files** during the walk
26    /// (mirrors `[ignore] tests`). What counts as a test is language-specific —
27    /// see [`LanguagePlugin::is_test_path`] — so the detection lives in the
28    /// plugin, not the CLI.
29    pub ignore_tests: bool,
30    /// Free-form key/value options. A plugin reads its own keys, ignores the rest.
31    pub options: Options,
32}
33
34/// A Prompt-Generator preset (a refactoring principle): a ready-to-paste AI
35/// instruction plus how the UI seeds the node selection for it. The orchestrator
36/// builds a generic default set and hands it to [`LanguagePlugin::presets`],
37/// which may pass it through, edit, drop or extend per language.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct Preset {
40    /// Stable id / short code shown on the button (e.g. `"ADP"`).
41    pub id: String,
42    /// Button label (usually the id).
43    pub label: String,
44    /// Full principle title (first heading of the generated prompt).
45    pub title: String,
46    /// The prompt body (Markdown, language-neutral by default).
47    pub prompt: String,
48    /// Link to the full principle doc, if any.
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    pub doc_url: Option<String>,
51    /// The metric the recommended-node list sorts by (an attribute key, or the
52    /// pseudo-metric `"cycle"`).
53    pub sort_metric: String,
54    /// Which connection sets the preset pre-selects: any of `"in"`/`"out"`/`"common"`.
55    #[serde(default, skip_serializing_if = "Vec::is_empty")]
56    pub connections: Vec<String>,
57}
58
59pub trait LanguagePlugin {
60    /// Canonical name, e.g. `"rust"`. Used by `--plugin` and recorded in the
61    /// snapshot. Each plugin has exactly one name (js and ts are separate).
62    fn name(&self) -> &str;
63
64    /// Can this plugin parse `workspace` (honoring `input`)?
65    fn detect(&self, workspace: &Path, input: &PluginInput) -> bool;
66
67    /// Levels this plugin can produce, each carrying its edge-kind / attribute /
68    /// node-kind / cycle-kind semantics.
69    fn levels(&self) -> Vec<Level>;
70
71    /// Parse the workspace into a graph AT `level` (by name). **Structure only**:
72    /// nodes (with their structural attributes) + edges. Metrics are added
73    /// downstream. When `input.ignore_tests` is set, the plugin must drop its
74    /// own test files here (it knows the language's conventions; see
75    /// [`is_test_path`](Self::is_test_path)).
76    fn analyze(&self, workspace: &Path, level: &str, input: &PluginInput) -> Result<Graph>;
77
78    /// Does this workspace-relative path (forward-slashed, no leading `./`) name
79    /// a **test** file in this language? Used to drop tests during the walk when
80    /// `PluginInput::ignore_tests` is set. Default: nothing is a test.
81    fn is_test_path(&self, _rel_path: &str) -> bool {
82        false
83    }
84
85    /// Toolchain versions to record in the snapshot, e.g. `[("rustc", "1.88.0")]`.
86    fn versions(&self, _workspace: &Path, _input: &PluginInput) -> Vec<(String, String)> {
87        Vec::new()
88    }
89
90    /// Transform the orchestrator's generic default presets for this language.
91    /// Default: pass them through unchanged. A plugin may reword a `prompt`,
92    /// change a `sort_metric`, drop a preset, or add language-specific ones.
93    fn presets(&self, defaults: Vec<Preset>, _input: &PluginInput) -> Vec<Preset> {
94        defaults
95    }
96
97    /// Language-calibrated per-metric thresholds (attribute key → tiers). The
98    /// orchestrator overlays these onto the attribute specs. Default: none.
99    fn thresholds(&self) -> BTreeMap<String, Thresholds> {
100        BTreeMap::new()
101    }
102}