z39/types/
bib1.rs

1//! Bib1 Attribute Set Types and Values
2//!
3//! https://www.loc.gov/z3950/agency/defns/bib1.html
4use crate::error::{LocalError, LocalResult};
5use crate::types::pdu::*;
6
7// Make working with the larger enums easier.
8use strum::IntoEnumIterator;
9use strum_macros::EnumIter;
10
11// Local shortcut
12fn err(type_: &str, val: u32) -> LocalError {
13    LocalError::ProtocolError(format!("No bib1::{type_} found with value '{val}'"))
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
17pub enum Attribute {
18    Use = 1,
19    Relation,
20    Position,
21    Structure,
22    Truncation,
23    Completeness,
24    Sorting,
25}
26
27impl TryFrom<u32> for Attribute {
28    type Error = LocalError;
29    fn try_from(n: u32) -> LocalResult<Self> {
30        Self::iter()
31            .find(|a| *a as u32 == n)
32            .ok_or_else(|| err("Attribute", n))
33    }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
37pub enum Use {
38    PersonalName = 1,
39    CorporateName,
40    ConferenceName,
41    Title,
42    TitleSeries,
43    TitleUniform,
44    Isbn,
45    Issn,
46    LcCardNumber,
47    BnbCardNumber,
48    BgfNumber,
49    LocalNumber,
50    DeweyClassification,
51    UdcClassification,
52    BlissClassification,
53    LcCallNumber,
54    NlmCallNumber,
55    NalCallNumber,
56    MosCallNumber,
57    LocalClassification,
58    SubjectHeading,
59    SubjectRameau,
60    BdiIndexSubject,
61    InspecSubject,
62    MeshSubject,
63    PaSubject,
64    LcSubjectHeading,
65    RvmSubjectHeading,
66    LocalSubjectIndex,
67    Date,
68    DateOfPublication,
69    DateOfAcquisition,
70    TitleKey,
71    TitleCollective,
72    TitleParallel,
73    TitleCover,
74    TitleAddedTitlePage,
75    TitleCaption,
76    TitleRunning,
77    TitleSpine,
78    TitleOtherVariant,
79    TitleFormer,
80    TitleAbbreviated,
81    TitleExpanded,
82    SubjectPrecis,
83    SubjectRswk,
84    SubjectSubdivision,
85    NumberNatlBiblio,
86    NumberLegalDeposit,
87    NumberGovtPub,
88    NumberMusicPublisher,
89    NumberDb,
90    NumberLocalCall,
91    CodeLanguage,
92    CodeGeographic,
93    CodeInstitution,
94    NameAndTitle,
95    NameGeographic,
96    PlacePublication,
97    Coden,
98    MicroformGeneration,
99    Abstract,
100    Note,
101    AuthorTitle = 1000,
102    RecordType,
103    Name,
104    Author,
105    AuthorNamePersonal,
106    AuthorNameCorporate,
107    AuthorNameConference,
108    IdentifierStandard,
109    SubjectLcChildrens,
110    SubjectNamePersonal,
111    BodyOfText,
112    DateTimeAddedToDb,
113    DateTimeLastModified,
114    AuthorityFormatId,
115    ConceptText,
116    ConceptReference,
117    Any,
118    ServerChoice,
119    Publisher,
120    RecordSource,
121    Editor,
122    BibLevel,
123    GeographicClass,
124    IndexedBy,
125    MapScale,
126    MusicKey,
127    RelatedPeriodical,
128    ReportNumber,
129    StockNumber,
130    ThematicNumber,
131    MaterialType,
132    DocId,
133    HostItem,
134    ContentType,
135    Anywhere,
136    AuthorTitleSubject,
137}
138
139impl From<Use> for AttributeElement {
140    fn from(u: Use) -> Self {
141        Self {
142            attribute_set: None,
143            attribute_type: Attribute::Use as u32,
144            attribute_value: AttributeValue::Numeric(u as u32),
145        }
146    }
147}
148
149impl TryFrom<u32> for Use {
150    type Error = LocalError;
151    fn try_from(n: u32) -> LocalResult<Self> {
152        Self::iter()
153            .find(|a| *a as u32 == n)
154            .ok_or_else(|| err("Use", n))
155    }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
159pub enum Relation {
160    LessThan = 1,
161    LessThanOrEqual,
162    Equal,
163    GreaterOrEqual,
164    GreaterThan,
165    NotEqual,
166    Phonetic = 100,
167    Stem,
168    Relevance,
169    AlwaysMatches,
170}
171
172impl TryFrom<u32> for Relation {
173    type Error = LocalError;
174    fn try_from(n: u32) -> LocalResult<Self> {
175        Self::iter()
176            .find(|a| *a as u32 == n)
177            .ok_or_else(|| err("Relation", n))
178    }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
182pub enum Position {
183    FirstInField = 1,
184    FirstInSubfield,
185    AnyPositionInField,
186}
187
188impl TryFrom<u32> for Position {
189    type Error = LocalError;
190    fn try_from(n: u32) -> LocalResult<Self> {
191        Self::iter()
192            .find(|a| *a as u32 == n)
193            .ok_or_else(|| err("Position", n))
194    }
195}
196
197#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
198pub enum Structure {
199    Phrase = 1,
200    Word,
201    Key,
202    Year,
203    DateNormalized,
204    WordList,
205    DateUnNormalized = 100,
206    NameNormalized,
207    NameUnNormalized,
208    Structure,
209    Urx,
210    FreeFormText,
211    DocumentText,
212    LocalNumber,
213    String,
214    NumericString,
215}
216
217impl TryFrom<u32> for Structure {
218    type Error = LocalError;
219    fn try_from(n: u32) -> LocalResult<Self> {
220        Self::iter()
221            .find(|a| *a as u32 == n)
222            .ok_or_else(|| err("Structure", n))
223    }
224}
225
226#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
227pub enum Truncation {
228    RightTruncation = 1,
229    LeftTruncation,
230    LeftAndRightTruncation,
231    DoNotTruncate = 100,
232    Process,
233    RegExpr1,
234    RegExpr2,
235    ProcessAlt, // ??
236}
237
238impl TryFrom<u32> for Truncation {
239    type Error = LocalError;
240    fn try_from(n: u32) -> LocalResult<Self> {
241        Self::iter()
242            .find(|a| *a as u32 == n)
243            .ok_or_else(|| err("Truncation", n))
244    }
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
248pub enum Completeness {
249    IncompleteSubfield = 1,
250    CompleteSubfield,
251    CompleteField,
252}
253
254impl TryFrom<u32> for Completeness {
255    type Error = LocalError;
256    fn try_from(n: u32) -> LocalResult<Self> {
257        Self::iter()
258            .find(|a| *a as u32 == n)
259            .ok_or_else(|| err("Completeness", n))
260    }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
264pub enum Sorting {
265    Ascending = 1,
266    Descending,
267}
268
269impl TryFrom<u32> for Sorting {
270    type Error = LocalError;
271    fn try_from(n: u32) -> LocalResult<Self> {
272        Self::iter()
273            .find(|a| *a as u32 == n)
274            .ok_or_else(|| err("Sorting", n))
275    }
276}
277
278/// Turn a z39 AttributeElement into a string of the form AttributeType=AttributeValue,
279/// where type and value are derived from the enum labels.
280///
281/// Complex attribute values are debug-stringified in place.
282///
283/// ```
284/// use z39::types::bib1::*;
285/// use z39::types::pdu::*;
286///
287/// let attr = AttributeElement {
288///     attribute_set: None,
289///     attribute_type: Attribute::Structure as u32,
290///     attribute_value: AttributeValue::Numeric(Structure::WordList as u32),
291/// };
292///
293/// assert_eq!(stringify_attribute(&attr).unwrap(), "Structure=WordList");
294/// ```
295pub fn stringify_attribute(a: &AttributeElement) -> LocalResult<String> {
296    let attr_type = Attribute::try_from(a.attribute_type)?;
297
298    let numeric_value = match &a.attribute_value {
299        AttributeValue::Numeric(n) => *n,
300        AttributeValue::Complex(c) => return Ok(format!("{attr_type:?}={c:?}")),
301    };
302
303    Ok(match attr_type {
304        Attribute::Use => format!("{attr_type:?}={:?}", Use::try_from(numeric_value)?),
305        Attribute::Relation => format!("{attr_type:?}={:?}", Relation::try_from(numeric_value)?),
306        Attribute::Position => format!("{attr_type:?}={:?}", Position::try_from(numeric_value)?),
307        Attribute::Structure => format!("{attr_type:?}={:?}", Structure::try_from(numeric_value)?),
308        Attribute::Truncation => {
309            format!("{attr_type:?}={:?}", Truncation::try_from(numeric_value)?)
310        }
311        Attribute::Completeness => {
312            format!("{attr_type:?}={:?}", Completeness::try_from(numeric_value)?)
313        }
314        Attribute::Sorting => format!("{attr_type:?}={:?}", Sorting::try_from(numeric_value)?),
315    })
316}