Skip to main content

citum_schema_data/reference/
ctors.rs

1/*
2SPDX-License-Identifier: MIT OR Apache-2.0
3SPDX-FileCopyrightText: © 2023-2026 Bruce D'Arcus and Citum contributors
4*/
5
6//! Construction helpers for [`InputReference`].
7//!
8//! Hosts the transitional PascalCase constructors (kept until the
9//! `csl26-1bdr` cutover lands), the snake_case `from_boxed_*` helpers, and the
10//! private `from_known<T>` used by the serde dispatcher.
11
12use serde::Deserialize;
13use serde_json::Value as JsonValue;
14
15use super::types::legal::{Brief, Hearing, LegalCase, Regulation, Statute, Treaty};
16use super::types::specialized::{
17    AudioVisualWork, Classic, Dataset, Event, Patent, Software, Standard,
18};
19use super::types::structural::{
20    Collection, CollectionComponent, Monograph, Serial, SerialComponent,
21};
22use super::{ClassExtension, InputReference, UnknownClassData};
23
24macro_rules! boxed_reference_constructor {
25    ($fn_name:ident, $ty:ty, $variant:ident) => {
26        fn $fn_name(reference: Box<$ty>) -> Self {
27            Self {
28                extension: ClassExtension::$variant(reference),
29                identifiers: Default::default(),
30            }
31        }
32    };
33}
34
35impl InputReference {
36    pub(super) fn from_known<T>(
37        class_extension: impl FnOnce(Box<T>) -> ClassExtension,
38        value: JsonValue,
39    ) -> Result<Self, serde_json::Error>
40    where
41        T: for<'de> Deserialize<'de>,
42    {
43        // Defensive: the dispatcher in `deserialize_reference_body` only ever
44        // hands us a `JsonValue::Object`; reject anything else with a schema
45        // error rather than letting `from_value` produce an opaque type error.
46        if !matches!(&value, JsonValue::Object(_)) {
47            return Err(<serde_json::Error as serde::de::Error>::custom(
48                "reference body must be a JSON object",
49            ));
50        }
51        let inner = serde_json::from_value(value)?;
52        Ok(Self {
53            extension: class_extension(Box::new(inner)),
54            identifiers: Default::default(),
55        })
56    }
57
58    // ────────────────────────────────────────────────────────────────────
59    // Transitional PascalCase constructors.
60    //
61    // These exist to keep call sites that used to construct an `InputReference`
62    // enum variant working unchanged through the discriminator cutover
63    // (bean: csl26-1bdr). They are NOT the long-term public surface and
64    // SHOULD NOT be relied upon by new code — use a `Box::new(<Class> { … })`
65    // value plus a future snake_case factory once one is exposed.
66    //
67    // TODO(csl26-1bdr): replace these 19 functions with idiomatic snake_case
68    // constructors and drop the transitional set. Tracked by parent epic.
69    // ────────────────────────────────────────────────────────────────────
70
71    /// Construct a monograph reference (transitional; see block note above).
72    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
73    #[must_use]
74    pub fn Monograph(reference: Box<Monograph>) -> Self {
75        Self::from_boxed_monograph(reference)
76    }
77
78    /// Construct a collection-component reference.
79    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
80    #[must_use]
81    pub fn CollectionComponent(reference: Box<CollectionComponent>) -> Self {
82        Self::from_boxed_collection_component(reference)
83    }
84
85    /// Construct a serial-component reference.
86    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
87    #[must_use]
88    pub fn SerialComponent(reference: Box<SerialComponent>) -> Self {
89        Self::from_boxed_serial_component(reference)
90    }
91
92    /// Construct a collection reference.
93    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
94    #[must_use]
95    pub fn Collection(reference: Box<Collection>) -> Self {
96        Self::from_boxed_collection(reference)
97    }
98
99    /// Construct a serial reference.
100    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
101    #[must_use]
102    pub fn Serial(reference: Box<Serial>) -> Self {
103        Self::from_boxed_serial(reference)
104    }
105
106    /// Construct a legal-case reference.
107    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
108    #[must_use]
109    pub fn LegalCase(reference: Box<LegalCase>) -> Self {
110        Self::from_boxed_legal_case(reference)
111    }
112
113    /// Construct a statute reference.
114    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
115    #[must_use]
116    pub fn Statute(reference: Box<Statute>) -> Self {
117        Self::from_boxed_statute(reference)
118    }
119
120    /// Construct a treaty reference.
121    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
122    #[must_use]
123    pub fn Treaty(reference: Box<Treaty>) -> Self {
124        Self::from_boxed_treaty(reference)
125    }
126
127    /// Construct a hearing reference.
128    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
129    #[must_use]
130    pub fn Hearing(reference: Box<Hearing>) -> Self {
131        Self::from_boxed_hearing(reference)
132    }
133
134    /// Construct a regulation reference.
135    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
136    #[must_use]
137    pub fn Regulation(reference: Box<Regulation>) -> Self {
138        Self::from_boxed_regulation(reference)
139    }
140
141    /// Construct a brief reference.
142    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
143    #[must_use]
144    pub fn Brief(reference: Box<Brief>) -> Self {
145        Self::from_boxed_brief(reference)
146    }
147
148    /// Construct a classic reference.
149    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
150    #[must_use]
151    pub fn Classic(reference: Box<Classic>) -> Self {
152        Self::from_boxed_classic(reference)
153    }
154
155    /// Construct a patent reference.
156    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
157    #[must_use]
158    pub fn Patent(reference: Box<Patent>) -> Self {
159        Self::from_boxed_patent(reference)
160    }
161
162    /// Construct a dataset reference.
163    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
164    #[must_use]
165    pub fn Dataset(reference: Box<Dataset>) -> Self {
166        Self::from_boxed_dataset(reference)
167    }
168
169    /// Construct a standard reference.
170    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
171    #[must_use]
172    pub fn Standard(reference: Box<Standard>) -> Self {
173        Self::from_boxed_standard(reference)
174    }
175
176    /// Construct a software reference.
177    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
178    #[must_use]
179    pub fn Software(reference: Box<Software>) -> Self {
180        Self::from_boxed_software(reference)
181    }
182
183    /// Construct an event reference.
184    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
185    #[must_use]
186    pub fn Event(reference: Box<Event>) -> Self {
187        Self::from_boxed_event(reference)
188    }
189
190    /// Construct an audio-visual reference.
191    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
192    #[must_use]
193    pub fn AudioVisual(reference: Box<AudioVisualWork>) -> Self {
194        Self::from_boxed_audio_visual(reference)
195    }
196
197    /// Construct an unknown-class reference.
198    #[allow(non_snake_case, reason = "Transitional compatibility constructor")]
199    #[must_use]
200    pub fn Unknown(reference: Box<UnknownClassData>) -> Self {
201        Self {
202            extension: ClassExtension::Unknown(reference),
203            identifiers: Default::default(),
204        }
205    }
206
207    boxed_reference_constructor!(from_boxed_monograph, Monograph, Monograph);
208    boxed_reference_constructor!(
209        from_boxed_collection_component,
210        CollectionComponent,
211        CollectionComponent
212    );
213    boxed_reference_constructor!(
214        from_boxed_serial_component,
215        SerialComponent,
216        SerialComponent
217    );
218    boxed_reference_constructor!(from_boxed_collection, Collection, Collection);
219    boxed_reference_constructor!(from_boxed_serial, Serial, Serial);
220    boxed_reference_constructor!(from_boxed_legal_case, LegalCase, LegalCase);
221    boxed_reference_constructor!(from_boxed_statute, Statute, Statute);
222    boxed_reference_constructor!(from_boxed_treaty, Treaty, Treaty);
223    boxed_reference_constructor!(from_boxed_hearing, Hearing, Hearing);
224    boxed_reference_constructor!(from_boxed_regulation, Regulation, Regulation);
225    boxed_reference_constructor!(from_boxed_brief, Brief, Brief);
226    boxed_reference_constructor!(from_boxed_classic, Classic, Classic);
227    boxed_reference_constructor!(from_boxed_patent, Patent, Patent);
228    boxed_reference_constructor!(from_boxed_dataset, Dataset, Dataset);
229    boxed_reference_constructor!(from_boxed_standard, Standard, Standard);
230    boxed_reference_constructor!(from_boxed_software, Software, Software);
231    boxed_reference_constructor!(from_boxed_event, Event, Event);
232    boxed_reference_constructor!(from_boxed_audio_visual, AudioVisualWork, AudioVisual);
233}