Skip to main content

citum_schema_data/reference/
classes.rs

1/*
2SPDX-License-Identifier: MIT OR Apache-2.0
3SPDX-FileCopyrightText: © 2023-2026 Bruce D'Arcus and Citum contributors
4*/
5
6//! Reference class discriminator types and shared dispatch helpers.
7
8#[cfg(feature = "bindings")]
9use specta::Type;
10
11use serde::{Deserialize, Serialize};
12
13use super::input::UnknownClassData;
14use super::types::legal::{Brief, Hearing, LegalCase, Regulation, Statute, Treaty};
15use super::types::specialized::{
16    AudioVisualWork, Classic, Dataset, Event, Patent, Software, Standard,
17};
18use super::types::structural::{
19    Collection, CollectionComponent, Monograph, Serial, SerialComponent,
20};
21
22/// Typed class discriminator returned by [`super::InputReference::class`].
23#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
25#[cfg_attr(feature = "bindings", derive(Type))]
26#[serde(rename_all = "kebab-case")]
27pub enum ReferenceClass {
28    /// A monograph, such as a book or report.
29    Monograph,
30    /// A component of a larger monographic collection.
31    CollectionComponent,
32    /// A component of a larger serial publication.
33    SerialComponent,
34    /// A collection of works, such as an anthology or proceedings.
35    Collection,
36    /// A serial publication, such as a journal or newspaper.
37    Serial,
38    /// A legal case.
39    LegalCase,
40    /// A statute or legislative act.
41    Statute,
42    /// An international treaty or agreement.
43    Treaty,
44    /// A legislative or administrative hearing.
45    Hearing,
46    /// An administrative regulation.
47    Regulation,
48    /// A legal brief or filing.
49    Brief,
50    /// A classic work with standard citation forms.
51    Classic,
52    /// A patent.
53    Patent,
54    /// A research dataset.
55    Dataset,
56    /// A technical standard or specification.
57    Standard,
58    /// Software or source code.
59    Software,
60    /// An event such as a conference, performance, or broadcast.
61    Event,
62    /// An audio-visual work.
63    AudioVisual,
64    /// A class string not known by this version.
65    ///
66    /// `#[serde(skip)]`: an `Unknown(...)` variant cannot serialize directly
67    /// through the derived `Serialize` impl on `ReferenceClass`. The class
68    /// string is preserved on the wire via [`UnknownClassData::class`] inside
69    /// the surrounding `InputReference`, not via this enum standalone.
70    #[serde(skip)]
71    Unknown(String),
72}
73
74impl ReferenceClass {
75    /// Known class names in their wire-format spelling.
76    pub const KNOWN: &'static [&'static str] = &[
77        "monograph",
78        "collection-component",
79        "serial-component",
80        "collection",
81        "serial",
82        "legal-case",
83        "statute",
84        "treaty",
85        "hearing",
86        "regulation",
87        "brief",
88        "classic",
89        "patent",
90        "dataset",
91        "standard",
92        "software",
93        "event",
94        "audio-visual",
95    ];
96
97    /// Return the known discriminator for a wire-format class name.
98    #[must_use]
99    pub(crate) fn from_known_name(class: &str) -> Option<Self> {
100        match class {
101            "monograph" => Some(Self::Monograph),
102            "collection-component" => Some(Self::CollectionComponent),
103            "serial-component" => Some(Self::SerialComponent),
104            "collection" => Some(Self::Collection),
105            "serial" => Some(Self::Serial),
106            "legal-case" => Some(Self::LegalCase),
107            "statute" => Some(Self::Statute),
108            "treaty" => Some(Self::Treaty),
109            "hearing" => Some(Self::Hearing),
110            "regulation" => Some(Self::Regulation),
111            "brief" => Some(Self::Brief),
112            "classic" => Some(Self::Classic),
113            "patent" => Some(Self::Patent),
114            "dataset" => Some(Self::Dataset),
115            "standard" => Some(Self::Standard),
116            "software" => Some(Self::Software),
117            "event" => Some(Self::Event),
118            "audio-visual" => Some(Self::AudioVisual),
119            _ => None,
120        }
121    }
122
123    /// Return this class's wire-format spelling.
124    #[cfg(feature = "schema")]
125    #[must_use]
126    pub(crate) fn name(&self) -> &str {
127        match self {
128            Self::Monograph => "monograph",
129            Self::CollectionComponent => "collection-component",
130            Self::SerialComponent => "serial-component",
131            Self::Collection => "collection",
132            Self::Serial => "serial",
133            Self::LegalCase => "legal-case",
134            Self::Statute => "statute",
135            Self::Treaty => "treaty",
136            Self::Hearing => "hearing",
137            Self::Regulation => "regulation",
138            Self::Brief => "brief",
139            Self::Classic => "classic",
140            Self::Patent => "patent",
141            Self::Dataset => "dataset",
142            Self::Standard => "standard",
143            Self::Software => "software",
144            Self::Event => "event",
145            Self::AudioVisual => "audio-visual",
146            Self::Unknown(class) => class,
147        }
148    }
149}
150
151/// Class-specific overlay stored inside [`super::InputReference`].
152#[derive(Debug, Clone, PartialEq)]
153#[cfg_attr(feature = "bindings", derive(Type))]
154pub enum ClassExtension {
155    /// Monograph-specific payload.
156    Monograph(Box<Monograph>),
157    /// Collection-component-specific payload.
158    CollectionComponent(Box<CollectionComponent>),
159    /// Serial-component-specific payload.
160    SerialComponent(Box<SerialComponent>),
161    /// Collection-specific payload.
162    Collection(Box<Collection>),
163    /// Serial-specific payload.
164    Serial(Box<Serial>),
165    /// Legal-case-specific payload.
166    LegalCase(Box<LegalCase>),
167    /// Statute-specific payload.
168    Statute(Box<Statute>),
169    /// Treaty-specific payload.
170    Treaty(Box<Treaty>),
171    /// Hearing-specific payload.
172    Hearing(Box<Hearing>),
173    /// Regulation-specific payload.
174    Regulation(Box<Regulation>),
175    /// Brief-specific payload.
176    Brief(Box<Brief>),
177    /// Classic-work-specific payload.
178    Classic(Box<Classic>),
179    /// Patent-specific payload.
180    Patent(Box<Patent>),
181    /// Dataset-specific payload.
182    Dataset(Box<Dataset>),
183    /// Standard-specific payload.
184    Standard(Box<Standard>),
185    /// Software-specific payload.
186    Software(Box<Software>),
187    /// Event-specific payload.
188    Event(Box<Event>),
189    /// Audio-visual-specific payload.
190    AudioVisual(Box<AudioVisualWork>),
191    /// Unknown-class payload.
192    Unknown(Box<UnknownClassData>),
193}
194
195impl ClassExtension {
196    /// Return the typed discriminator corresponding to this extension.
197    #[must_use]
198    pub(crate) fn reference_class(&self) -> ReferenceClass {
199        match self {
200            Self::Monograph(_) => ReferenceClass::Monograph,
201            Self::CollectionComponent(_) => ReferenceClass::CollectionComponent,
202            Self::SerialComponent(_) => ReferenceClass::SerialComponent,
203            Self::Collection(_) => ReferenceClass::Collection,
204            Self::Serial(_) => ReferenceClass::Serial,
205            Self::LegalCase(_) => ReferenceClass::LegalCase,
206            Self::Statute(_) => ReferenceClass::Statute,
207            Self::Treaty(_) => ReferenceClass::Treaty,
208            Self::Hearing(_) => ReferenceClass::Hearing,
209            Self::Regulation(_) => ReferenceClass::Regulation,
210            Self::Brief(_) => ReferenceClass::Brief,
211            Self::Classic(_) => ReferenceClass::Classic,
212            Self::Patent(_) => ReferenceClass::Patent,
213            Self::Dataset(_) => ReferenceClass::Dataset,
214            Self::Standard(_) => ReferenceClass::Standard,
215            Self::Software(_) => ReferenceClass::Software,
216            Self::Event(_) => ReferenceClass::Event,
217            Self::AudioVisual(_) => ReferenceClass::AudioVisual,
218            Self::Unknown(data) => ReferenceClass::Unknown(data.class.clone()),
219        }
220    }
221
222    /// Return the wire-format class string for this extension.
223    #[must_use]
224    pub(crate) fn class_name(&self) -> &str {
225        match self {
226            Self::Monograph(_) => "monograph",
227            Self::CollectionComponent(_) => "collection-component",
228            Self::SerialComponent(_) => "serial-component",
229            Self::Collection(_) => "collection",
230            Self::Serial(_) => "serial",
231            Self::LegalCase(_) => "legal-case",
232            Self::Statute(_) => "statute",
233            Self::Treaty(_) => "treaty",
234            Self::Hearing(_) => "hearing",
235            Self::Regulation(_) => "regulation",
236            Self::Brief(_) => "brief",
237            Self::Classic(_) => "classic",
238            Self::Patent(_) => "patent",
239            Self::Dataset(_) => "dataset",
240            Self::Standard(_) => "standard",
241            Self::Software(_) => "software",
242            Self::Event(_) => "event",
243            Self::AudioVisual(_) => "audio-visual",
244            Self::Unknown(data) => &data.class,
245        }
246    }
247}
248
249macro_rules! class_dispatch {
250    (
251        $extension:expr,
252        |$reference:ident| $body:expr,
253        audio_visual($audio_visual:ident) => $audio_visual_body:expr,
254        unknown($unknown:pat) => $unknown_body:expr
255    ) => {
256        match $extension {
257            ClassExtension::Monograph($reference) => $body,
258            ClassExtension::CollectionComponent($reference) => $body,
259            ClassExtension::SerialComponent($reference) => $body,
260            ClassExtension::Collection($reference) => $body,
261            ClassExtension::Serial($reference) => $body,
262            ClassExtension::LegalCase($reference) => $body,
263            ClassExtension::Statute($reference) => $body,
264            ClassExtension::Treaty($reference) => $body,
265            ClassExtension::Hearing($reference) => $body,
266            ClassExtension::Regulation($reference) => $body,
267            ClassExtension::Brief($reference) => $body,
268            ClassExtension::Classic($reference) => $body,
269            ClassExtension::Patent($reference) => $body,
270            ClassExtension::Dataset($reference) => $body,
271            ClassExtension::Standard($reference) => $body,
272            ClassExtension::Software($reference) => $body,
273            ClassExtension::Event($reference) => $body,
274            ClassExtension::AudioVisual($audio_visual) => $audio_visual_body,
275            ClassExtension::Unknown($unknown) => $unknown_body,
276        }
277    };
278
279    ($extension:expr, |$reference:ident| $body:expr, unknown($unknown:pat) => $unknown_body:expr) => {
280        match $extension {
281            ClassExtension::Monograph($reference) => $body,
282            ClassExtension::CollectionComponent($reference) => $body,
283            ClassExtension::SerialComponent($reference) => $body,
284            ClassExtension::Collection($reference) => $body,
285            ClassExtension::Serial($reference) => $body,
286            ClassExtension::LegalCase($reference) => $body,
287            ClassExtension::Statute($reference) => $body,
288            ClassExtension::Treaty($reference) => $body,
289            ClassExtension::Hearing($reference) => $body,
290            ClassExtension::Regulation($reference) => $body,
291            ClassExtension::Brief($reference) => $body,
292            ClassExtension::Classic($reference) => $body,
293            ClassExtension::Patent($reference) => $body,
294            ClassExtension::Dataset($reference) => $body,
295            ClassExtension::Standard($reference) => $body,
296            ClassExtension::Software($reference) => $body,
297            ClassExtension::Event($reference) => $body,
298            ClassExtension::AudioVisual($reference) => $body,
299            ClassExtension::Unknown($unknown) => $unknown_body,
300        }
301    };
302}
303
304pub(crate) use class_dispatch;