Skip to main content

citum_engine/api/
types.rs

1/*
2SPDX-License-Identifier: MIT OR Apache-2.0
3SPDX-FileCopyrightText: © 2023-2026 Bruce D'Arcus and Citum contributors
4*/
5
6//! Request and response types for the interactive document API.
7
8use citum_schema::data::citation::{CitationLocator, IntegralNameState};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12pub use citum_schema_data::AbbreviationMap;
13
14/// Severity level for a structured diagnostic.
15#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
16#[serde(rename_all = "lowercase")]
17pub enum WarningLevel {
18    /// Non-fatal diagnostic message.
19    Warning,
20    /// Error that may affect output quality or correctness.
21    Error,
22}
23
24/// A structured diagnostic warning returned alongside formatted output.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct Warning {
27    /// The severity level.
28    pub level: WarningLevel,
29    /// A machine-readable error code.
30    pub code: String,
31    /// The citation ID this warning pertains to, if any.
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub citation_id: Option<String>,
34    /// The reference ID this warning pertains to, if any.
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub ref_id: Option<String>,
37    /// A human-readable diagnostic message.
38    pub message: String,
39}
40
41/// Supported output format kinds.
42#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
43#[serde(rename_all = "lowercase")]
44pub enum OutputFormatKind {
45    /// Plain text (default).
46    #[default]
47    Plain,
48    /// HTML markup.
49    Html,
50    /// Djot inline markup.
51    Djot,
52    /// LaTeX markup.
53    Latex,
54    /// Typst markup.
55    Typst,
56}
57
58/// Controls how annotation text is rendered in an annotated bibliography.
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct AnnotationStyle {
61    /// Markup format for annotation text. Default: Djot.
62    #[serde(default)]
63    pub format: AnnotationFormat,
64}
65
66impl Default for AnnotationStyle {
67    fn default() -> Self {
68        Self {
69            format: AnnotationFormat::Djot,
70        }
71    }
72}
73
74/// Markup format for annotation text.
75#[derive(Debug, Clone, Default, Serialize, Deserialize)]
76#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
77#[serde(rename_all = "snake_case")]
78pub enum AnnotationFormat {
79    /// Parse annotation as djot inline markup (default).
80    #[default]
81    Djot,
82    /// Treat annotation as plain text with no markup interpretation.
83    Plain,
84    /// Parse annotation as org-mode markup.
85    Org,
86}
87
88/// A single item within a citation occurrence.
89///
90/// Maps to `CitationItem` from `citum-schema-data`.
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct CitationOccurrenceItem {
93    /// The reference ID (citekey) being cited.
94    pub id: String,
95    /// Optional locator (pinpoint citation).
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub locator: Option<CitationLocator>,
98    /// Optional prefix text before this item.
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub prefix: Option<String>,
101    /// Optional suffix text after this item.
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub suffix: Option<String>,
104    /// Explicit integral (narrative) name state override.
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub integral_name_state: Option<IntegralNameState>,
107}
108
109impl From<CitationOccurrenceItem> for citum_schema::data::citation::CitationItem {
110    fn from(item: CitationOccurrenceItem) -> Self {
111        Self {
112            id: item.id,
113            locator: item.locator,
114            prefix: item.prefix,
115            suffix: item.suffix,
116            integral_name_state: item.integral_name_state,
117        }
118    }
119}
120
121/// A citation occurrence in the document.
122///
123/// Maps to `Citation` from `citum-schema-data`.
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct CitationOccurrence {
126    /// Stable identifier for this citation in the document.
127    pub id: String,
128    /// The citation items (references cited together).
129    pub items: Vec<CitationOccurrenceItem>,
130    /// The citation mode (integral vs non-integral).
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub mode: Option<citum_schema::data::citation::CitationMode>,
133    /// Footnote/endnote number for note-based styles.
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub note_number: Option<u32>,
136    /// Whether to suppress the author across all items.
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub suppress_author: Option<bool>,
139    /// Whether this is a compound-numeric group.
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub grouped: Option<bool>,
142    /// Optional prefix text before all formatted items.
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub prefix: Option<String>,
145    /// Optional suffix text after all formatted items.
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub suffix: Option<String>,
148}
149
150impl From<CitationOccurrence> for citum_schema::data::citation::Citation {
151    fn from(occ: CitationOccurrence) -> Self {
152        Self {
153            id: Some(occ.id),
154            items: occ.items.into_iter().map(|item| item.into()).collect(),
155            mode: occ.mode.unwrap_or_default(),
156            note_number: occ.note_number,
157            suppress_author: occ.suppress_author.unwrap_or(false),
158            grouped: occ.grouped.unwrap_or(false),
159            prefix: occ.prefix,
160            suffix: occ.suffix,
161            position: None, // Assigned by processor
162        }
163    }
164}
165
166/// Document-level configuration for rendering.
167///
168/// Controls rendering behavior that belongs to the document rather than the style.
169#[derive(Debug, Clone, Default, Serialize, Deserialize)]
170#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
171#[serde(default)]
172pub struct DocumentOptions {
173    /// Override or replace style-defined bibliography grouping.
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub bibliography_groups: Option<Vec<citum_schema::grouping::BibliographyGroup>>,
176    /// Automatic bibliography partitioning by script or language.
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub sort_partitioning: Option<citum_schema::options::BibliographySortPartitioning>,
179    /// Document-level narrative citation rules.
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub integral_name_memory: Option<crate::processor::document::DocumentIntegralNameOverride>,
182    /// Whether to output semantic markup (HTML spans, Djot attributes).
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub show_semantics: Option<bool>,
185    /// Whether to annotate semantic wrappers with source template indices.
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub inject_ast_indices: Option<bool>,
188    /// Reference ID to annotation text mapping.
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub annotations: Option<HashMap<String, String>>,
191    /// Format for annotation text (djot, plain, org).
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub annotation_format: Option<AnnotationFormat>,
194    /// Map from full rendered strings to their abbreviations.
195    #[serde(alias = "abbreviation-map", skip_serializing_if = "Option::is_none")]
196    pub abbreviation_map: Option<AbbreviationMap>,
197}
198
199/// A single formatted citation.
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct FormattedCitation {
202    /// The citation identifier from the input.
203    pub id: String,
204    /// The formatted citation text.
205    pub text: String,
206    /// Referenced entry IDs from this citation.
207    pub ref_ids: Vec<String>,
208}
209
210/// Metadata extracted from a bibliography entry for interactivity.
211#[derive(Debug, Clone, Default, Serialize, Deserialize)]
212pub struct EntryMetadata {
213    /// Rendered author(s) string.
214    pub author: String,
215    /// Rendered year string.
216    pub year: String,
217    /// Rendered title string.
218    pub title: String,
219}
220
221/// A single bibliography entry.
222#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct BibliographyEntry {
224    /// The reference ID.
225    pub id: String,
226    /// The formatted entry text.
227    pub text: String,
228    /// Extracted metadata for interactivity.
229    pub metadata: EntryMetadata,
230}
231
232/// A formatted bibliography.
233#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct FormattedBibliography {
235    /// The output format used.
236    pub format: OutputFormatKind,
237    /// The complete formatted bibliography content.
238    pub content: String,
239    /// Individual entries with metadata.
240    pub entries: Vec<BibliographyEntry>,
241}