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