Skip to main content

tokmd_types/
lib.rs

1//! # tokmd-types
2//!
3//! **Tier 0 (Core Types)**
4//!
5//! This crate defines the core data structures and contracts for `tokmd`.
6//! It contains only data types, Serde definitions, and `schema_version`.
7//!
8//! ## Stability Policy
9//!
10//! **JSON-first stability**: The primary contract is the JSON schema, not Rust struct literals.
11//!
12//! - **JSON consumers**: Stable. New fields have sensible defaults; removed/renamed fields
13//!   bump `SCHEMA_VERSION`.
14//! - **Rust library consumers**: Semi-stable. New fields may be added in minor versions,
15//!   which can break struct literal construction. Use `Default` + field mutation or
16//!   `..Default::default()` patterns for forward compatibility.
17//!
18//! If you need strict Rust API stability, pin to an exact version.
19//!
20//! ## What belongs here
21//! * Pure data structs (Receipts, Rows, Reports)
22//! * Serialization/Deserialization logic
23//! * Stability markers (SCHEMA_VERSION)
24//!
25//! ## What does NOT belong here
26//! * File I/O
27//! * CLI argument parsing
28//! * Complex business logic
29//! * Tokei dependencies
30
31use std::path::PathBuf;
32
33use serde::{Deserialize, Serialize};
34
35/// The current schema version for all receipt types.
36pub const SCHEMA_VERSION: u32 = 2;
37
38/// A small totals struct shared by summary outputs.
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
40pub struct Totals {
41    pub code: usize,
42    pub lines: usize,
43    pub files: usize,
44    pub bytes: usize,
45    pub tokens: usize,
46    pub avg_lines: usize,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
50pub struct LangRow {
51    pub lang: String,
52    pub code: usize,
53    pub lines: usize,
54    pub files: usize,
55    pub bytes: usize,
56    pub tokens: usize,
57    pub avg_lines: usize,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct LangReport {
62    pub rows: Vec<LangRow>,
63    pub total: Totals,
64    pub with_files: bool,
65    pub children: ChildrenMode,
66    pub top: usize,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
70pub struct ModuleRow {
71    pub module: String,
72    pub code: usize,
73    pub lines: usize,
74    pub files: usize,
75    pub bytes: usize,
76    pub tokens: usize,
77    pub avg_lines: usize,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ModuleReport {
82    pub rows: Vec<ModuleRow>,
83    pub total: Totals,
84    pub module_roots: Vec<String>,
85    pub module_depth: usize,
86    pub children: ChildIncludeMode,
87    pub top: usize,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
91#[serde(rename_all = "snake_case")]
92pub enum FileKind {
93    Parent,
94    Child,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
98pub struct FileRow {
99    pub path: String,
100    pub module: String,
101    pub lang: String,
102    pub kind: FileKind,
103    pub code: usize,
104    pub comments: usize,
105    pub blanks: usize,
106    pub lines: usize,
107    pub bytes: usize,
108    pub tokens: usize,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ExportData {
113    pub rows: Vec<FileRow>,
114    pub module_roots: Vec<String>,
115    pub module_depth: usize,
116    pub children: ChildIncludeMode,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct RunReceipt {
121    pub schema_version: u32,
122    pub generated_at_ms: u128,
123    pub lang_file: String,
124    pub module_file: String,
125    pub export_file: String,
126    // We could store the scan args here too
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "snake_case")]
131pub enum ScanStatus {
132    Complete,
133    Partial,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize, Default)]
137pub struct ToolInfo {
138    pub name: String,
139    pub version: String,
140}
141
142impl ToolInfo {
143    pub fn current() -> Self {
144        Self {
145            name: "tokmd".to_string(),
146            version: env!("CARGO_PKG_VERSION").to_string(),
147        }
148    }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct ScanArgs {
153    pub paths: Vec<String>,
154    pub excluded: Vec<String>,
155    /// True if `excluded` patterns were redacted (replaced with hashes).
156    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
157    pub excluded_redacted: bool,
158    pub config: ConfigMode,
159    pub hidden: bool,
160    pub no_ignore: bool,
161    pub no_ignore_parent: bool,
162    pub no_ignore_dot: bool,
163    pub no_ignore_vcs: bool,
164    pub treat_doc_strings_as_comments: bool,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct LangArgsMeta {
169    pub format: String,
170    pub top: usize,
171    pub with_files: bool,
172    pub children: ChildrenMode,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct LangReceipt {
177    pub schema_version: u32,
178    pub generated_at_ms: u128,
179    pub tool: ToolInfo,
180    pub mode: String, // "lang"
181    pub status: ScanStatus,
182    pub warnings: Vec<String>,
183    pub scan: ScanArgs,
184    pub args: LangArgsMeta,
185    #[serde(flatten)]
186    pub report: LangReport,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct ModuleArgsMeta {
191    pub format: String,
192    pub module_roots: Vec<String>,
193    pub module_depth: usize,
194    pub children: ChildIncludeMode,
195    pub top: usize,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct ModuleReceipt {
200    pub schema_version: u32,
201    pub generated_at_ms: u128,
202    pub tool: ToolInfo,
203    pub mode: String, // "module"
204    pub status: ScanStatus,
205    pub warnings: Vec<String>,
206    pub scan: ScanArgs,
207    pub args: ModuleArgsMeta,
208    #[serde(flatten)]
209    pub report: ModuleReport,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ExportArgsMeta {
214    pub format: ExportFormat,
215    pub module_roots: Vec<String>,
216    pub module_depth: usize,
217    pub children: ChildIncludeMode,
218    pub min_code: usize,
219    pub max_rows: usize,
220    pub redact: RedactMode,
221    pub strip_prefix: Option<String>,
222    /// True if `strip_prefix` was redacted (replaced with a hash).
223    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
224    pub strip_prefix_redacted: bool,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct ExportReceipt {
229    pub schema_version: u32,
230    pub generated_at_ms: u128,
231    pub tool: ToolInfo,
232    pub mode: String, // "export"
233    pub status: ScanStatus,
234    pub warnings: Vec<String>,
235    pub scan: ScanArgs,
236    pub args: ExportArgsMeta,
237    #[serde(flatten)]
238    pub data: ExportData,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct LangArgs {
243    pub paths: Vec<PathBuf>,
244    pub format: TableFormat,
245    pub top: usize,
246    pub files: bool,
247    pub children: ChildrenMode,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct ModuleArgs {
252    pub paths: Vec<PathBuf>,
253    pub format: TableFormat,
254    pub top: usize,
255    pub module_roots: Vec<String>,
256    pub module_depth: usize,
257    pub children: ChildIncludeMode,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ExportArgs {
262    pub paths: Vec<PathBuf>,
263    pub format: ExportFormat,
264    pub out: Option<PathBuf>,
265    pub module_roots: Vec<String>,
266    pub module_depth: usize,
267    pub children: ChildIncludeMode,
268    pub min_code: usize,
269    pub max_rows: usize,
270    pub redact: RedactMode,
271    pub meta: bool,
272    pub strip_prefix: Option<PathBuf>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct ContextReceipt {
277    pub schema_version: u32,
278    pub generated_at_ms: u128,
279    pub tool: ToolInfo,
280    pub mode: String,
281    pub budget_tokens: usize,
282    pub used_tokens: usize,
283    pub utilization_pct: f64,
284    pub strategy: String,
285    pub rank_by: String,
286    pub file_count: usize,
287    pub files: Vec<ContextFileRow>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct ContextFileRow {
292    pub path: String,
293    pub module: String,
294    pub lang: String,
295    pub tokens: usize,
296    pub code: usize,
297    pub lines: usize,
298    pub bytes: usize,
299    pub value: usize,
300}
301
302// -----------------------
303// Diff types
304// -----------------------
305
306/// A row in the diff output showing changes for a single language.
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
308pub struct DiffRow {
309    pub lang: String,
310    pub old_code: usize,
311    pub new_code: usize,
312    pub delta_code: i64,
313    pub old_lines: usize,
314    pub new_lines: usize,
315    pub delta_lines: i64,
316    pub old_files: usize,
317    pub new_files: usize,
318    pub delta_files: i64,
319    pub old_bytes: usize,
320    pub new_bytes: usize,
321    pub delta_bytes: i64,
322    pub old_tokens: usize,
323    pub new_tokens: usize,
324    pub delta_tokens: i64,
325}
326
327/// Aggregate totals for the diff.
328#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
329pub struct DiffTotals {
330    pub old_code: usize,
331    pub new_code: usize,
332    pub delta_code: i64,
333    pub old_lines: usize,
334    pub new_lines: usize,
335    pub delta_lines: i64,
336    pub old_files: usize,
337    pub new_files: usize,
338    pub delta_files: i64,
339    pub old_bytes: usize,
340    pub new_bytes: usize,
341    pub delta_bytes: i64,
342    pub old_tokens: usize,
343    pub new_tokens: usize,
344    pub delta_tokens: i64,
345}
346
347/// JSON receipt for diff output with envelope metadata.
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct DiffReceipt {
350    pub schema_version: u32,
351    pub generated_at_ms: u128,
352    pub tool: ToolInfo,
353    pub mode: String,
354    pub from_source: String,
355    pub to_source: String,
356    pub diff_rows: Vec<DiffRow>,
357    pub totals: DiffTotals,
358}
359
360// -----------------------------------------------------------------------------
361// Enums shared with CLI (moved from tokmd-config)
362// -----------------------------------------------------------------------------
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
365#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
366#[serde(rename_all = "kebab-case")]
367pub enum TableFormat {
368    /// Markdown table (great for pasting into ChatGPT).
369    Md,
370    /// Tab-separated values (good for piping to other tools).
371    Tsv,
372    /// JSON (compact).
373    Json,
374}
375
376#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
377#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
378#[serde(rename_all = "kebab-case")]
379pub enum ExportFormat {
380    /// CSV with a header row.
381    Csv,
382    /// One JSON object per line.
383    Jsonl,
384    /// A single JSON array.
385    Json,
386    /// CycloneDX 1.6 JSON SBOM format.
387    Cyclonedx,
388}
389
390#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
391#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
392#[serde(rename_all = "kebab-case")]
393pub enum ConfigMode {
394    /// Read `tokei.toml` / `.tokeirc` if present.
395    #[default]
396    Auto,
397    /// Ignore config files.
398    None,
399}
400
401#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
402#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
403#[serde(rename_all = "kebab-case")]
404pub enum ChildrenMode {
405    /// Merge embedded content into the parent language totals.
406    Collapse,
407    /// Show embedded languages as separate "(embedded)" rows.
408    Separate,
409}
410
411#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
412#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
413#[serde(rename_all = "kebab-case")]
414pub enum ChildIncludeMode {
415    /// Include embedded languages as separate contributions.
416    Separate,
417    /// Ignore embedded languages.
418    ParentsOnly,
419}
420
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
422#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
423#[serde(rename_all = "kebab-case")]
424pub enum RedactMode {
425    /// Do not redact.
426    None,
427    /// Redact file paths.
428    Paths,
429    /// Redact file paths and module names.
430    All,
431}
432
433/// Log record for context command JSONL append mode.
434/// Contains metadata only (not file contents) for lightweight logging.
435#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct ContextLogRecord {
437    pub schema_version: u32,
438    pub generated_at_ms: u128,
439    pub tool: ToolInfo,
440    pub budget_tokens: usize,
441    pub used_tokens: usize,
442    pub utilization_pct: f64,
443    pub strategy: String,
444    pub rank_by: String,
445    pub file_count: usize,
446    pub total_bytes: usize,
447    pub output_destination: String,
448}