Skip to main content

talon_core/indexing/
output.rs

1//! Indexing tool output types.
2
3use serde::{Deserialize, Serialize};
4
5use crate::contracts::{ContainerPath, VaultPath};
6
7use super::input::InspectCheck;
8
9/// Sync status.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "kebab-case")]
12pub enum SyncStatus {
13    /// Sync completed.
14    Ok,
15    /// Sync partially completed.
16    Partial,
17    /// Sync failed.
18    Failed,
19    /// Sync busy (lock held by another process).
20    Busy,
21}
22
23/// Sync response.
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26#[allow(clippy::struct_excessive_bools)]
27pub struct SyncResponse {
28    /// Whether the sync completed.
29    pub completed: bool,
30    /// Status label.
31    pub status: SyncStatus,
32    /// Whether lexical-only mode was requested.
33    pub fast: bool,
34    /// Whether vector reset was requested.
35    pub force: bool,
36    /// Whether the index database was rebuilt before syncing.
37    pub rebuild: bool,
38    /// Number of paths in scope.
39    pub path_count: u32,
40    /// Indexed notes.
41    pub indexed: u32,
42    /// Skipped notes.
43    pub skipped: u32,
44    /// Deleted notes.
45    pub deleted: u32,
46    /// Notes embedded during this sync pass.
47    pub embedded: u32,
48    /// Notes that failed embedding.
49    pub embed_failed: u32,
50    /// True if any vector dimension differed mid-pass; semantic search is
51    /// disabled until the next consistent pass succeeds.
52    pub dimension_mismatch: bool,
53    /// Operator-facing remediation hint (present when the embed pass detected
54    /// a recoverable problem; e.g. dim mismatch tells the user to re-run
55    /// with `--force`).
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub embed_remediation: Option<String>,
58    /// Up to 20 redacted detail strings from the embed pass.
59    #[serde(default, skip_serializing_if = "Vec::is_empty")]
60    pub embed_diagnostics: Vec<String>,
61    /// Graph artifact stats from sync-time graph rebuild.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub graph: Option<crate::graph::GraphBuildStats>,
64    /// Duration in milliseconds.
65    pub duration_ms: u64,
66}
67
68/// Status state.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(rename_all = "kebab-case")]
71pub enum StatusState {
72    /// Disabled by config.
73    Disabled,
74    /// Config is invalid.
75    ConfigError,
76    /// Ready to serve requests.
77    Ready,
78}
79
80/// Index statistics.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct IndexStats {
84    /// Active notes.
85    pub active_notes: u32,
86    /// Indexed chunks.
87    pub chunk_count: u32,
88    /// Failed embeddings.
89    pub failed_embeddings: u32,
90    /// Vector dimensions, if known.
91    pub vector_dimensions: Option<u16>,
92}
93
94/// Scope report from status.
95#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct ScopeReport {
98    /// Total number of configured scopes.
99    pub total_scopes: u32,
100    /// Default scope names.
101    pub default_scopes: Vec<String>,
102    /// Files not matching any scope.
103    pub unscoped_count: u32,
104}
105
106/// Status response.
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct StatusResponse {
110    /// Readiness state.
111    pub state: StatusState,
112    /// Whether Talon is enabled.
113    pub enabled: bool,
114    /// Optional reason for non-ready states.
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub reason: Option<String>,
117    /// Container mount path.
118    pub container_mount: ContainerPath,
119    /// Index version.
120    pub index_version: String,
121    /// Index statistics.
122    pub index: IndexStats,
123    /// Scope report.
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub scopes: Option<ScopeReport>,
126    /// Resolved vault path.
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub vault_path: Option<String>,
129    /// Config file path used.
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub config_path: Option<String>,
132    /// Database path used.
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub db_path: Option<String>,
135}
136
137/// A single inspect finding.
138#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct InspectFinding {
141    /// Inspect check that produced this finding.
142    pub check: InspectCheck,
143    /// Vault-relative path of the file.
144    pub path: VaultPath,
145    /// Description of the finding.
146    pub message: String,
147    /// Line number, if applicable.
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub line: Option<u32>,
150}
151
152/// Inspect check response.
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154#[serde(rename_all = "camelCase")]
155pub struct InspectResponse {
156    /// Vault root (absolute container path).
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub vault: Option<ContainerPath>,
159    /// The check that was run.
160    pub check: InspectCheck,
161    /// Inspect findings.
162    pub findings: Vec<InspectFinding>,
163}