# reachgraph-plugin-api — public API surface
#
# GENERATED. Regenerate with `cargo xtask public-api --bless`, which is a
# deliberate command on purpose: a snapshot that repairs itself when the test
# fails asserts nothing (plan-02 §7.2.1).
#
# `public_api_snapshot_matches` asserts this file byte for byte. A diff here is
# a change to the plugin contract — an added field, a changed signature, a
# leaked type — and it is a diff somebody has to read. The assertion makes the
# change visible; it cannot make anyone look.
#
# Doc comments and function bodies are normalised away; derives, bounds and
# signatures are not.

## reachgraph_plugin_api

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ArtifactFile {
    pub path: String,
    pub bytes: Vec<u8>,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Capability {
    Symbols,
    Edges,
    Roots,
    Classify,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Category {
    FirstParty,
    Generated,
    WorkspaceSibling,
    ThirdParty,
    Stdlib,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ContractId(pub String);

#[derive(Clone, Debug)]
pub struct Coverage {
    pub contracts: Vec<ContractId>,
    pub versions: Vec<VersionKey>,
    pub unexamined_contracts: Vec<UnexaminedContract>,
}

#[derive(Clone, Debug)]
pub struct Detection {
    pub marker_files: &'static [&'static str],
    pub extensions: &'static [&'static str],
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Direction {
    Served,
    Consumed,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DocFormat {
    Plain,
    Markdown,
}

#[derive(Clone, Debug)]
pub struct Edge {
    pub from: NodeId,
    pub to: EdgeTarget,
    pub call_site: Option<SourceRange>,
    pub provenance: Provenance,
    pub inference_mode: InferenceMode,
}

#[derive(Clone, Debug)]
pub enum EdgeTarget {
    Resolved(NodeId),
    Unresolved { name: String, candidates: Vec<NodeId> },
}

#[derive(Clone, Debug)]
pub struct GraphView {
    pub nodes: Vec<Node>,
    pub edges: Vec<Edge>,
    pub roots: Vec<Root>,
    pub plugins: Vec<PluginDescriptor>,
    pub coverage: IndexCoverage,
}

#[derive(Clone, Debug)]
pub struct IndexCoverage {
    pub contracts: Vec<ContractId>,
    pub versions: Vec<VersionKey>,
    pub roots_total: usize,
    pub roots_bound: usize,
    pub unbound_roots: Vec<UnboundRoot>,
    pub unexamined_contracts: Vec<UnexaminedContract>,
    pub units_indexed: Vec<UnitId>,
    pub plugins: Vec<PluginId>,
    pub traversal_terminal_categories: Vec<Category>,
    pub partial: bool,
    pub notes: Vec<String>,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum InferenceMode {
    Resolved,
    Lexical,
    TypeInferred,
    Enclosure,
}

#[derive(Clone, Debug)]
pub struct Node {
    pub id: NodeId,
    pub symbol: Option<Symbol>,
    pub category: Option<Category>,
    pub unit: Option<UnitId>,
    pub depth: Option<u32>,
    pub frontier: bool,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct NodeId {
    pub plugin: PluginId,
    pub raw: String,
}

#[derive(Clone, Debug)]
pub struct PluginDescriptor {
    pub id: PluginId,
    pub position_encoding: PositionEncoding,
    pub capabilities: Vec<Capability>,
}

#[derive(Debug)]
pub enum PluginError {
    Io { plugin: PluginId, path: PathBuf, source: std::io::Error },
    Parse { plugin: PluginId, path: PathBuf, detail: String },
    UnknownUnit { plugin: PluginId, unit: UnitId },
    UnknownNode { plugin: PluginId, node: NodeId },
    Engine { plugin: PluginId, engine: String, detail: String },
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct PluginId(pub &'static str);

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PositionEncoding {
    Utf8Bytes,
    Utf16CodeUnits,
    Utf32CodePoints,
}

#[derive(Clone, Debug)]
pub enum Preflight {
    Ok,
    Warned { reason: String, remediation: String },
    Failed { reason: String, remediation: String },
}

#[derive(Clone, Debug)]
pub struct Provenance {
    pub plugin: PluginId,
    pub engine: String,
}

pub struct Registered {}

pub struct Registration<P: Plugin + 'static> {}

#[derive(Default)]
pub struct Registry {}

#[derive(Clone, PartialEq, Eq, Debug)]
pub enum RegistryError {
    DuplicateId { plugin: PluginId },
    CapabilityNotDeclared { plugin: PluginId, capability: Capability },
    CapabilityNotProvided { plugin: PluginId, capability: Capability },
}

#[derive(Debug)]
pub enum RenderError {
    Sink { path: String, source: std::io::Error },
    Refused { reason: String },
}

#[derive(Clone, Copy, Debug)]
pub struct RenderInput<'a> {
    pub view: &'a GraphView,
    pub shards: &'a [Shard],
    pub artifact: &'a [ArtifactFile],
}

#[derive(Clone, Debug)]
pub struct Root {
    pub contract: ContractId,
    pub version: Option<String>,
    pub service: String,
    pub operation: String,
    pub direction: Direction,
    pub join_key: String,
    pub binding: RootBinding,
}

#[derive(Clone, Debug)]
pub enum RootBinding {
    Bound(NodeId),
    Unbound { reason: String },
}

#[derive(Clone, Debug)]
pub struct Shard {
    pub root: Root,
    pub depth_limit: Option<u32>,
    pub frontier: Vec<NodeId>,
    pub view: GraphView,
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SourceRange {
    pub file: PathBuf,
    pub span: Option<Span>,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Span {
    pub start: u32,
    pub end: u32,
}

#[derive(Clone, Debug)]
pub struct Symbol {
    pub id: NodeId,
    pub name: String,
    pub kind: SymbolKind,
    pub raw_kind: String,
    pub range: SourceRange,
    pub doc: Option<String>,
    pub doc_format: DocFormat,
    pub container: Option<NodeId>,
    pub is_test: bool,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SymbolKind {
    Function,
    Method,
    Type,
    Module,
    Field,
    Other,
}

#[derive(Clone, Debug)]
pub struct UnboundRoot {
    pub contract: ContractId,
    pub version: Option<String>,
    pub service: String,
    pub operation: String,
    pub direction: Direction,
    pub reason: String,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnexaminedContract {
    pub contract: ContractId,
    pub reason: String,
}

#[derive(Clone, Debug)]
pub struct Unit {
    pub id: UnitId,
    pub display_name: String,
    pub root: PathBuf,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct UnitId(pub String);

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct VersionKey {
    pub contract: ContractId,
    pub version: Option<String>,
}

pub trait Classifier: Plugin {
    fn classify(&self, path: &Path, unit: &Unit) -> Category;
}

pub trait EdgeProvider: LanguagePlugin {
    fn edges_in(&self, unit: &Unit) -> Result<Vec<Edge>, PluginError>;
    fn edges_from(&self, node: &NodeId) -> Result<Vec<Edge>, PluginError>;
}

pub trait LanguagePlugin: Plugin {
    fn discover_units(&self, root: &Path) -> Result<Vec<Unit>, PluginError>;
}

pub trait OutputSink {
    fn write(&mut self, relative_path: &str, bytes: &[u8]) -> std::io::Result<()>;
}

pub trait Plugin: Send + Sync {
    fn id(&self) -> PluginId;
    fn provides(&self) -> &[Capability];
    fn position_encoding(&self) -> PositionEncoding;
    fn detection(&self) -> Detection;
    fn preflight(&self, root: &Path) -> Preflight;
    fn notes(&self) -> Vec<String>;
}

pub trait Renderer: Send + Sync {
    fn id(&self) -> PluginId;
    fn owns(&self) -> &[&'static str];
    fn render(
        &self,
        input: &RenderInput<'_>,
        sink: &mut dyn OutputSink,
    ) -> Result<(), RenderError>;
}

pub trait RootProvider: Plugin {
    fn roots(
        &self,
        repo_root: &Path,
        symbols: &dyn SymbolIndex,
    ) -> Result<Vec<Root>, PluginError>;
    fn coverage(&self) -> Coverage;
}

pub trait SymbolIndex {
    fn by_name(&self, name: &str) -> Vec<&Symbol>;
    fn in_file(&self, path: &Path) -> Vec<&Symbol>;
    fn get(&self, id: &NodeId) -> Option<&Symbol>;
}

pub trait SymbolProvider: LanguagePlugin {
    fn symbols_in(&self, unit: &Unit) -> Result<Vec<Symbol>, PluginError>;
}

impl GraphView {
    pub fn new(
        nodes: Vec<Node>,
        edges: Vec<Edge>,
        roots: Vec<Root>,
        plugins: Vec<PluginDescriptor>,
        coverage: IndexCoverage,
    ) -> Self {}
    pub fn node(&self, id: &NodeId) -> Option<&Node> {}
    pub fn depth_of(&self, id: &NodeId) -> Option<u32> {}
    pub fn max_depth(&self) -> Option<u32> {}
    pub fn nodes_at_depth(&self, depth: u32) -> Vec<&Node> {}
    pub fn container_of(&self, id: &NodeId) -> Option<&Node> {}
    pub fn container_chain(&self, id: &NodeId) -> Vec<&Node> {}
    pub fn contained_in(&self, id: &NodeId) -> Vec<&Node> {}
    pub fn nodes_in_unit(&self, unit: &UnitId) -> Vec<&Node> {}
    pub fn out_edges(&self, id: &NodeId) -> Vec<&Edge> {}
    pub fn in_edges(&self, id: &NodeId) -> Vec<&Edge> {}
}

impl Registered {
    pub fn plugin(&self) -> &dyn Plugin {}
    pub fn symbols(&self) -> Option<&dyn SymbolProvider> {}
    pub fn edges(&self) -> Option<&dyn EdgeProvider> {}
    pub fn roots(&self) -> Option<&dyn RootProvider> {}
    pub fn classifier(&self) -> Option<&dyn Classifier> {}
}

impl<P: Plugin + 'static> Registration<P> {
    pub fn of(plugin: P) -> Self {}
    pub fn symbols(mut self) -> Self
    where
        P: SymbolProvider,
    {}
    pub fn edges(mut self) -> Self
    where
        P: EdgeProvider,
    {}
    pub fn roots(mut self) -> Self
    where
        P: RootProvider,
    {}
    pub fn classifier(mut self) -> Self
    where
        P: Classifier,
    {}
}

impl Registry {
    pub fn new() -> Self {}
    pub fn register<P: Plugin + 'static>(
        &mut self,
        registration: Registration<P>,
    ) -> Result<(), RegistryError> {}
    pub fn plugins(&self) -> impl Iterator<Item = &Registered> {}
    pub fn detect(&self, root: &Path) -> Vec<&Registered> {}
    pub fn select(&self, id: PluginId) -> Option<&Registered> {}
}

impl fmt::Debug for Registered {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}
}

impl fmt::Display for PluginError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}
}

impl fmt::Display for RegistryError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}
}

impl fmt::Display for RenderError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}
}

impl std::error::Error for PluginError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {}
}

impl std::error::Error for RenderError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {}
}
