code_ranker_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}