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    /// Explicit org-abbreviation state override.
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub org_abbreviation_state: Option<IntegralNameState>,
110}
111
112impl From<CitationOccurrenceItem> for citum_schema::data::citation::CitationItem {
113    fn from(item: CitationOccurrenceItem) -> Self {
114        Self {
115            id: item.id,
116            locator: item.locator,
117            prefix: item.prefix,
118            suffix: item.suffix,
119            integral_name_state: item.integral_name_state,
120            org_abbreviation_state: item.org_abbreviation_state,
121        }
122    }
123}
124
125/// A citation occurrence in the document.
126///
127/// Maps to `Citation` from `citum-schema-data`.
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct CitationOccurrence {
130    /// Stable identifier for this citation in the document.
131    pub id: String,
132    /// The citation items (references cited together).
133    pub items: Vec<CitationOccurrenceItem>,
134    /// The citation mode (integral vs non-integral).
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub mode: Option<citum_schema::data::citation::CitationMode>,
137    /// Footnote/endnote number for note-based styles.
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub note_number: Option<u32>,
140    /// Whether to suppress the author across all items.
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub suppress_author: Option<bool>,
143    /// Whether this is a compound-numeric group.
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub grouped: Option<bool>,
146    /// Optional prefix text before all formatted items.
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub prefix: Option<String>,
149    /// Optional suffix text after all formatted items.
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub suffix: Option<String>,
152}
153
154impl From<CitationOccurrence> for citum_schema::data::citation::Citation {
155    fn from(occ: CitationOccurrence) -> Self {
156        Self {
157            id: Some(occ.id),
158            items: occ.items.into_iter().map(|item| item.into()).collect(),
159            mode: occ.mode.unwrap_or_default(),
160            note_number: occ.note_number,
161            suppress_author: occ.suppress_author.unwrap_or(false),
162            grouped: occ.grouped.unwrap_or(false),
163            prefix: occ.prefix,
164            suffix: occ.suffix,
165            position: None, // Assigned by processor
166        }
167    }
168}
169
170/// Document-level configuration for rendering.
171///
172/// Controls rendering behavior that belongs to the document rather than the style.
173#[derive(Debug, Clone, Default, Serialize, Deserialize)]
174#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
175#[serde(default)]
176pub struct DocumentOptions {
177    /// Override or replace style-defined bibliography grouping.
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub bibliography_groups: Option<Vec<citum_schema::grouping::BibliographyGroup>>,
180    /// Automatic bibliography partitioning by script or language.
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub sort_partitioning: Option<citum_schema::options::BibliographySortPartitioning>,
183    /// Document-level narrative citation rules.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub integral_name_memory: Option<crate::processor::document::DocumentIntegralNameOverride>,
186    /// Whether to output semantic markup (HTML spans, Djot attributes).
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub show_semantics: Option<bool>,
189    /// Whether to annotate semantic wrappers with source template indices.
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub inject_ast_indices: Option<bool>,
192    /// Reference ID to annotation text mapping.
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub annotations: Option<HashMap<String, String>>,
195    /// Format for annotation text (djot, plain, org).
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub annotation_format: Option<AnnotationFormat>,
198    /// Map from full rendered strings to their abbreviations.
199    #[serde(alias = "abbreviation-map", skip_serializing_if = "Option::is_none")]
200    pub abbreviation_map: Option<AbbreviationMap>,
201}
202
203/// A single formatted citation.
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct FormattedCitation {
206    /// The citation identifier from the input.
207    pub id: String,
208    /// The formatted citation text.
209    pub text: String,
210    /// Referenced entry IDs from this citation.
211    pub ref_ids: Vec<String>,
212}
213
214/// Metadata extracted from a bibliography entry for interactivity.
215#[derive(Debug, Clone, Default, Serialize, Deserialize)]
216pub struct EntryMetadata {
217    /// Rendered author(s) string.
218    pub author: String,
219    /// Rendered year string.
220    pub year: String,
221    /// Rendered title string.
222    pub title: String,
223}
224
225/// A single bibliography entry.
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct BibliographyEntry {
228    /// The reference ID.
229    pub id: String,
230    /// The formatted entry text.
231    pub text: String,
232    /// Extracted metadata for interactivity.
233    pub metadata: EntryMetadata,
234}
235
236/// A formatted bibliography.
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct FormattedBibliography {
239    /// The output format used.
240    pub format: OutputFormatKind,
241    /// The complete formatted bibliography content.
242    pub content: String,
243    /// Individual entries with metadata.
244    pub entries: Vec<BibliographyEntry>,
245}