1use std::collections::BTreeMap;
8
9use serde::{Deserialize, Serialize};
10
11use crate::unit::Unit;
12
13#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct CodebaseIndex {
17 pub schema_version: String,
19 pub build: IndexBuild,
20 pub packages: Vec<PackageRecord>,
22 pub traceability: Traceability,
24 pub diagnostics: Diagnostics,
25}
26
27#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct IndexBuild {
31 pub indexer_id: String,
32 pub indexer_version: String,
33 pub repo_root: String,
34 pub content_hash: String,
36 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
40 pub slice_hashes: BTreeMap<String, String>,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(rename_all = "kebab-case")]
46pub enum PackageKind {
47 RustLib,
48 RustBin,
49 RustLibBin,
50 NpmPackage,
51 NpmWorkspace,
52}
53
54#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(rename_all = "camelCase")]
57pub struct PackageRecord {
58 pub name: String,
59 pub path: String,
61 pub kind: PackageKind,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub version: Option<String>,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub edition: Option<String>,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub spec_ref: Option<String>,
69}
70
71#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct Traceability {
75 pub mappings: Vec<TraceMapping>,
76 pub orphaned_specs: Vec<String>,
78 pub untraced_code: Vec<String>,
80}
81
82#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct TraceMapping {
86 pub spec_id: String,
87 #[serde(default, skip_serializing_if = "Option::is_none")]
88 pub spec_status: Option<String>,
89 #[serde(default, skip_serializing_if = "Vec::is_empty")]
90 pub depends_on: Vec<String>,
91 #[serde(default, skip_serializing_if = "Vec::is_empty")]
92 pub amends: Vec<String>,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub amendment_record: Option<String>,
95 pub implementing_paths: Vec<ImplementingPath>,
97 #[serde(default, skip_serializing_if = "Vec::is_empty")]
99 pub resolved_units: Vec<ResolvedUnit>,
100}
101
102#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
104#[serde(rename_all = "kebab-case")]
105pub enum TraceSource {
106 SpecEdge,
108 ManifestMetadata,
110 CommentHeader,
112 Multiple,
114}
115
116#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub struct ImplementingPath {
120 pub path: String,
121 pub source: TraceSource,
122}
123
124#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
126#[serde(rename_all = "snake_case")]
127pub enum SourceField {
128 Establishes,
129 Extends,
130 Refines,
131 Supersedes,
132 Amends,
133 CoAuthority,
134 Constrains,
135 References,
136}
137
138impl SourceField {
139 pub fn is_ownership(self) -> bool {
141 !matches!(self, SourceField::References)
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct ResolvedUnit {
149 pub unit: Unit,
150 pub source_field: SourceField,
151 pub ownership: bool,
153 pub locations: Vec<ResolvedLocation>,
155}
156
157#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
159#[serde(rename_all = "camelCase")]
160pub struct ResolvedLocation {
161 pub file: String,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
163 pub span: Option<LineSpan>,
164}
165
166#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
168#[serde(rename_all = "camelCase")]
169pub struct LineSpan {
170 pub start_line: usize,
171 pub end_line: usize,
172}
173
174impl LineSpan {
175 pub fn new(start_line: usize, end_line: usize) -> Self {
176 LineSpan {
177 start_line,
178 end_line,
179 }
180 }
181}
182
183#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
185#[serde(rename_all = "camelCase")]
186pub struct Diagnostics {
187 pub warnings: Vec<Diagnostic>,
188 pub errors: Vec<Diagnostic>,
189}
190
191impl Diagnostics {
192 pub fn is_empty(&self) -> bool {
194 self.warnings.is_empty() && self.errors.is_empty()
195 }
196}
197
198#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
210#[serde(rename_all = "camelCase")]
211pub struct IndexSpecShard {
212 pub schema_version: String,
214 pub shard_hash: String,
218 pub mapping: TraceMapping,
220 #[serde(default, skip_serializing_if = "Diagnostics::is_empty")]
223 pub diagnostics: Diagnostics,
224}
225
226#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
229#[serde(rename_all = "camelCase")]
230pub struct IndexPackageShard {
231 pub schema_version: String,
233 pub shard_hash: String,
236 pub package: PackageRecord,
238}
239
240#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
242#[serde(rename_all = "camelCase")]
243pub struct Diagnostic {
244 pub code: String,
245 pub message: String,
246 #[serde(default, skip_serializing_if = "Option::is_none")]
247 pub path: Option<String>,
248}