Skip to main content

dociium/
shared_types.rs

1//! Shared canonical type definitions for Dociium.
2//!
3//! Motivation:
4//! The project originally duplicated many structurally identical data
5//! structures across `doc_engine::types` and `index_core::types`, e.g.:
6//! - SourceLocation
7//! - ItemDoc
8//! - ImplItem / TraitImpl / TypeImpl
9//! - SymbolSearchResult
10//! - SearchIndexItem / SearchIndexData
11//! - SourceSnippet
12//!
13//! This module introduces *canonical* representations intended to be the
14//! single source of truth. Other modules can (incrementally) migrate to
15//! these definitions, minimizing maintenance burden and serialization /
16//! schema drift.
17//!
18//! Integration Plan (incremental, low‑risk):
19//! 1. Introduce `shared_types` (this file) with stable serde + schemars
20//!    (schema) derivations.
21//! 2. Add `pub mod shared_types;` to `lib.rs` (future patch).
22//! 3. Add `From` / `Into` impls in *calling* code rather than here to
23//!    avoid introducing circular compilation dependencies while both
24//!    legacy type sets still exist.
25//! 4. Gradually replace internal uses:
26//!      - Prefer `shared_types::SourceLocation` etc.
27//!      - Remove duplicated structs from `doc_engine::types` and
28//!        `index_core::types` once references are gone.
29//! 5. Expose via MCP schemas directly from this module for consistency.
30//!
31//! Design Notes:
32//! - Field names retain the most widely used naming style to keep JSON
33//!   stable (backwards compatibility).
34//! - `visibility` kept as `String` (enum later when rustdoc variants
35//!   fully enumerated).
36//! - `kind` also left as `String` for forward compatibility.
37//! - Optional fields kept as `Option<T>`; empty vectors prefer `Vec<T>`
38//!   not `Option<Vec<T>>` to simplify consumer logic.
39//! - Added `#[non_exhaustive]` where future evolution is likely.
40//!
41//! Future Enhancements:
42//! - Introduce strongly typed enums for `ItemKind`, `Visibility`,
43//!   `SymbolKind` with `serde(other)` fallbacks.
44//! - Attach rich lifetime / generics info to impls when upstream
45//!   extraction improves.
46//!
47//! This file is intentionally `dead_code`‑tolerant during migration.
48//!
49//! SPDX-License-Identifier: MIT OR Apache-2.0
50
51#![allow(dead_code)]
52
53use schemars::JsonSchema;
54use serde::{Deserialize, Serialize};
55
56/// Canonical source span reference inside a file.
57#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
58pub struct SourceLocation {
59    pub file: String,
60    pub line: u32,
61    pub column: u32,
62    pub end_line: Option<u32>,
63    pub end_column: Option<u32>,
64}
65
66impl SourceLocation {
67    pub fn single_point(file: impl Into<String>, line: u32, column: u32) -> Self {
68        Self {
69            file: file.into(),
70            line,
71            column,
72            end_line: None,
73            end_column: None,
74        }
75    }
76}
77
78/// Documentation & analysis metadata for a single symbol / item.
79#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
80pub struct ItemDoc {
81    pub path: String,
82    pub kind: String,
83    pub rendered_markdown: String,
84    pub source_location: Option<SourceLocation>,
85    pub visibility: String,
86    pub attributes: Vec<String>,
87    pub signature: Option<String>,
88    pub examples: Vec<String>,
89    pub see_also: Vec<String>,
90}
91
92/// Individual member (method / assoc item) inside an implementation block.
93#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
94pub struct ImplItem {
95    pub name: String,
96    pub kind: String,
97    pub signature: Option<String>,
98    pub doc: Option<String>,
99    pub source_location: Option<SourceLocation>,
100}
101
102/// Trait implementation (impl <Trait> for <Type>).
103#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
104pub struct TraitImpl {
105    pub for_type: String,
106    pub trait_path: String,
107    pub generics: Vec<String>,
108    pub where_clause: Option<String>,
109    pub source_span: Option<SourceLocation>,
110    pub impl_id: String,
111    pub items: Vec<ImplItem>,
112    pub is_blanket: bool,
113    pub is_synthetic: bool,
114}
115
116/// Implemented trait entry when listing traits *for* a type.
117#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
118pub struct TypeImpl {
119    pub trait_path: String,
120    pub generics: Vec<String>,
121    pub where_clause: Option<String>,
122    pub source_span: Option<SourceLocation>,
123    pub impl_id: String,
124    pub items: Vec<ImplItem>,
125    pub is_blanket: bool,
126    pub is_synthetic: bool,
127}
128
129/// Lightweight search index item (mirrors docs.rs structure).
130#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
131pub struct SearchIndexItem {
132    pub name: String,
133    pub kind: String,
134    pub path: String,
135    pub description: String,
136    pub parent_index: Option<usize>,
137}
138
139/// Crate‑scoped search index dataset (one version).
140#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
141pub struct SearchIndexData {
142    pub crate_name: String,
143    pub version: String,
144    pub items: Vec<SearchIndexItem>,
145    pub paths: Vec<String>,
146}
147
148/// Symbol search result (post‑query scoring).
149#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
150pub struct SymbolSearchResult {
151    pub path: String,
152    pub kind: String,
153    pub score: f32,
154    pub doc_summary: Option<String>,
155    pub source_location: Option<SourceLocation>,
156    pub visibility: String,
157    pub signature: Option<String>,
158    pub module_path: String,
159}
160
161/// Semantic search result for language-aware discovery (Python, etc.).
162#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
163pub struct SemanticSearchResult {
164    pub language: String,
165    pub package: String,
166    pub module_path: String,
167    pub item_name: String,
168    pub qualified_path: String,
169    pub kind: String,
170    pub file: String,
171    pub line: u32,
172    pub score: f32,
173    pub doc_preview: Option<String>,
174    pub signature: Option<String>,
175    pub source_preview: Option<String>,
176}
177
178/// Code snippet with context.
179#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
180pub struct SourceSnippet {
181    pub code: String,
182    pub file: String,
183    pub line_start: u32,
184    pub line_end: u32,
185    pub context_lines: u32,
186    pub highlighted_line: Option<u32>,
187    pub language: String,
188}
189
190/// Unified error domain stub (future: centralize).
191#[non_exhaustive]
192#[derive(Debug, thiserror::Error)]
193pub enum SharedTypeError {
194    #[error("Conversion failure: {0}")]
195    Conversion(String),
196}
197
198pub type SharedResult<T> = Result<T, SharedTypeError>;
199
200/// Helper trait for fallible conversion into shared canonical types.
201///
202/// Implementations can be added externally to avoid cyclic deps:
203/// e.g. impl TryIntoShared<shared_types::ItemDoc> for doc_engine::types::ItemDoc { ... }
204pub trait TryIntoShared<T> {
205    fn try_into_shared(self) -> SharedResult<T>;
206}
207
208/// Helper trait for converting *from* shared canonical forms.
209/// (Symmetric with TryIntoShared for clearer intent.)
210pub trait TryFromShared<T> {
211    fn try_from_shared(value: T) -> SharedResult<Self>
212    where
213        Self: Sized;
214}
215
216/// Macro to implement trivial identity conversions for the shared set.
217/// This reduces boilerplate if external code wants a uniform call site.
218macro_rules! impl_identity_shared {
219    ($($ty:ty),* $(,)?) => {
220        $(
221            impl TryIntoShared<$ty> for $ty {
222                fn try_into_shared(self) -> SharedResult<$ty> { Ok(self) }
223            }
224            impl TryFromShared<$ty> for $ty {
225                fn try_from_shared(value: $ty) -> SharedResult<Self> { Ok(value) }
226            }
227        )*
228    };
229}
230impl_identity_shared!(
231    SourceLocation,
232    ItemDoc,
233    ImplItem,
234    TraitImpl,
235    TypeImpl,
236    SearchIndexItem,
237    SearchIndexData,
238    SymbolSearchResult,
239    SemanticSearchResult,
240    SourceSnippet
241);
242
243/// Utility: map a collection with TryIntoShared.
244pub fn map_vec_try_into<S, T, I>(iter: I) -> SharedResult<Vec<T>>
245where
246    I: IntoIterator<Item = S>,
247    S: TryIntoShared<T>,
248{
249    iter.into_iter()
250        .map(|s| s.try_into_shared())
251        .collect::<SharedResult<Vec<T>>>()
252}
253
254/// Utility: shallow merge of doc summaries where the canonical result is missing.
255/// Returns updated target reference.
256pub fn backfill_doc_summary(target: &mut SymbolSearchResult, fallback: &SymbolSearchResult) {
257    if target.doc_summary.is_none() && fallback.doc_summary.is_some() {
258        target.doc_summary = fallback.doc_summary.clone();
259    }
260}