1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 //
5 // Copyright © 2018 Corporation for Digital Scholarship
6 
7 use super::terms::{TermForm, TermFormExtended, TextTermSelector};
8 use super::IsIndependent;
9 use crate::attr::EnumGetAttribute;
10 use crate::error::*;
11 use crate::locale::{Lang, Locale};
12 use crate::terms::LocatorType;
13 use crate::variables::*;
14 use crate::version::{CslVersionReq, Features};
15 use crate::SmartString;
16 use fnv::{FnvHashMap, FnvHashSet};
17 #[cfg(feature = "serde")]
18 use serde::Serialize;
19 use std::fmt;
20 use std::str::FromStr;
21 use std::sync::Arc;
22 
23 pub mod dependent;
24 pub mod info;
25 use info::Info;
26 
27 type TermPlural = bool;
28 type StripPeriods = bool;
29 type Quotes = bool;
30 
31 #[derive(Debug, Eq, Clone, PartialEq)]
32 pub enum TextSource {
33     Macro(SmartString),
34     Value(SmartString),
35     Variable(StandardVariable, VariableForm),
36     Term(TextTermSelector, TermPlural),
37 }
38 impl Default for TextSource {
default() -> Self39     fn default() -> Self {
40         TextSource::Value("".into())
41     }
42 }
43 
44 #[derive(Default, Debug, Eq, Clone, PartialEq)]
45 pub struct TextElement {
46     pub source: TextSource,
47     pub formatting: Option<Formatting>,
48     pub affixes: Option<Affixes>,
49     pub quotes: Quotes,
50     pub strip_periods: StripPeriods,
51     pub text_case: TextCase,
52     pub display: Option<DisplayMode>,
53 }
54 
55 #[derive(Debug, Eq, Clone, PartialEq)]
56 pub struct LabelElement {
57     pub variable: NumberVariable,
58     pub form: TermForm,
59     pub formatting: Option<Formatting>,
60     pub affixes: Option<Affixes>,
61     pub strip_periods: StripPeriods,
62     pub text_case: TextCase,
63     pub plural: Plural,
64 }
65 
66 #[derive(Debug, Eq, Clone, PartialEq)]
67 pub struct NumberElement {
68     pub variable: NumberVariable,
69     pub form: NumericForm,
70     pub formatting: Option<Formatting>,
71     pub affixes: Option<Affixes>,
72     pub text_case: TextCase,
73     pub display: Option<DisplayMode>,
74 }
75 
76 #[derive(Debug, Eq, Clone, PartialEq)]
77 pub enum Element {
78     /// <cs:text>
79     Text(TextElement),
80     /// <cs:label>
81     Label(LabelElement),
82     /// <cs:number>
83     Number(NumberElement),
84     /// <cs:group>
85     Group(Group),
86     /// <cs:choose>
87     /// Arc because the IR needs a reference to one, cloning deep trees is costly, and IR has
88     /// to be in a Salsa db that doesn't really support lifetimes.
89     Choose(Arc<Choose>),
90     /// <cs:names>
91     Names(Arc<Names>),
92     /// <cs:date>
93     Date(Arc<BodyDate>),
94 }
95 
96 #[derive(Debug, Eq, Clone, PartialEq)]
97 pub struct Group {
98     pub formatting: Option<Formatting>,
99     pub delimiter: Option<SmartString>,
100     pub affixes: Option<Affixes>,
101     pub elements: Vec<Element>,
102     pub display: Option<DisplayMode>,
103     /// CSL-M only
104     pub is_parallel: bool,
105 }
106 
107 #[derive(Debug, Clone, PartialEq, Eq)]
108 pub enum BodyDate {
109     Indep(IndependentDate),
110     Local(LocalizedDate),
111 }
112 
113 impl BodyDate {
variable(&self) -> DateVariable114     pub fn variable(&self) -> DateVariable {
115         match self {
116             BodyDate::Indep(i) => i.variable,
117             BodyDate::Local(l) => l.variable,
118         }
119     }
120 }
121 
122 /// e.g. for <text variable="title" form="short" />
123 #[derive(AsRefStr, EnumString, EnumProperty, Debug, Copy, Clone, PartialEq, Eq, Hash)]
124 #[strum(serialize_all = "kebab_case")]
125 pub enum VariableForm {
126     Long,
127     Short,
128 }
129 
130 impl EnumGetAttribute for VariableForm {}
131 impl Default for VariableForm {
default() -> Self132     fn default() -> Self {
133         VariableForm::Long
134     }
135 }
136 
137 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
138 #[strum(serialize_all = "kebab_case")]
139 pub enum NumericForm {
140     Numeric,
141     Ordinal,
142     Roman,
143     LongOrdinal,
144 }
145 
146 impl EnumGetAttribute for NumericForm {}
147 impl Default for NumericForm {
default() -> Self148     fn default() -> Self {
149         NumericForm::Numeric
150     }
151 }
152 
153 #[derive(Clone, PartialEq, Eq, Hash)]
154 pub struct Affixes {
155     pub prefix: SmartString,
156     pub suffix: SmartString,
157 }
158 
159 impl Default for Affixes {
default() -> Self160     fn default() -> Self {
161         Affixes {
162             prefix: "".into(),
163             suffix: "".into(),
164         }
165     }
166 }
167 
168 #[cfg_attr(feature = "serde", derive(Serialize))]
169 #[derive(Eq, Copy, Clone, Default, PartialEq, Hash)]
170 pub struct Formatting {
171     #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
172     pub font_style: Option<FontStyle>,
173     #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
174     pub font_variant: Option<FontVariant>,
175     #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
176     pub font_weight: Option<FontWeight>,
177     #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
178     pub vertical_alignment: Option<VerticalAlignment>,
179     #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
180     pub text_decoration: Option<TextDecoration>,
181     // TODO: put this somewhere else, like directly on text nodes?
182     // pub hyperlink: String,
183 }
184 
185 impl Formatting {
bold() -> Self186     pub fn bold() -> Self {
187         let mut f = Formatting::default();
188         f.font_weight = Some(FontWeight::Bold);
189         f
190     }
italic() -> Self191     pub fn italic() -> Self {
192         let mut f = Formatting::default();
193         f.font_style = Some(FontStyle::Italic);
194         f
195     }
small_caps() -> Self196     pub fn small_caps() -> Self {
197         let mut f = Formatting::default();
198         f.font_variant = Some(FontVariant::SmallCaps);
199         f
200     }
override_with(self, other: Self) -> Self201     pub fn override_with(self, other: Self) -> Self {
202         Formatting {
203             font_variant: other.font_variant.or(self.font_variant),
204             font_style: other.font_style.or(self.font_style),
205             font_weight: other.font_weight.or(self.font_weight),
206             vertical_alignment: other.vertical_alignment.or(self.vertical_alignment),
207             text_decoration: other.text_decoration.or(self.text_decoration),
208         }
209     }
210 }
211 
212 impl fmt::Debug for Affixes {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result213     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214         write!(f, "Affixes {{ ")?;
215         if !self.prefix.is_empty() {
216             write!(f, "prefix: {:?}, ", self.prefix)?;
217         }
218         if !self.suffix.is_empty() {
219             write!(f, "suffix: {:?}, ", self.suffix)?;
220         }
221         write!(f, "}}")
222     }
223 }
224 
225 impl fmt::Debug for Formatting {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result226     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227         write!(f, "Formatting {{ ")?;
228         if let Some(font_style) = self.font_style {
229             write!(f, "font_style: {:?}, ", font_style)?;
230         }
231         if let Some(font_variant) = self.font_variant {
232             write!(f, "font_variant: {:?}, ", font_variant)?;
233         }
234         if let Some(font_weight) = self.font_weight {
235             write!(f, "font_weight: {:?}, ", font_weight)?;
236         }
237         if let Some(text_decoration) = self.text_decoration {
238             write!(f, "text_decoration: {:?}, ", text_decoration)?;
239         }
240         if let Some(vertical_alignment) = self.vertical_alignment {
241             write!(f, "vertical_alignment: {:?}, ", vertical_alignment)?;
242         }
243         write!(f, "}}")
244     }
245 }
246 
247 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
248 #[cfg_attr(feature = "serde", derive(Serialize))]
249 #[strum(serialize_all = "kebab_case")]
250 pub enum DisplayMode {
251     Block,
252     LeftMargin,
253     RightInline,
254     Indent,
255 }
256 impl EnumGetAttribute for DisplayMode {}
257 
258 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
259 #[strum(serialize_all = "kebab_case")]
260 pub enum TextCase {
261     None,
262     Lowercase,
263     Uppercase,
264     CapitalizeFirst,
265     CapitalizeAll,
266     Sentence,
267     Title,
268 }
269 
270 impl EnumGetAttribute for TextCase {}
271 impl Default for TextCase {
default() -> Self272     fn default() -> Self {
273         TextCase::None
274     }
275 }
276 
277 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
278 #[cfg_attr(feature = "serde", derive(Serialize))]
279 #[strum(serialize_all = "kebab_case")]
280 pub enum FontStyle {
281     Normal,
282     Italic,
283     Oblique,
284 }
285 
286 impl EnumGetAttribute for FontStyle {}
287 impl Default for FontStyle {
default() -> Self288     fn default() -> Self {
289         FontStyle::Normal
290     }
291 }
292 
293 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
294 #[cfg_attr(feature = "serde", derive(Serialize))]
295 #[strum(serialize_all = "kebab_case")]
296 pub enum FontVariant {
297     Normal,
298     SmallCaps,
299 }
300 
301 impl EnumGetAttribute for FontVariant {}
302 impl Default for FontVariant {
default() -> Self303     fn default() -> Self {
304         FontVariant::Normal
305     }
306 }
307 
308 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
309 #[cfg_attr(feature = "serde", derive(Serialize))]
310 #[strum(serialize_all = "kebab_case")]
311 pub enum FontWeight {
312     Normal,
313     Bold,
314     Light,
315 }
316 
317 impl EnumGetAttribute for FontWeight {}
318 impl Default for FontWeight {
default() -> Self319     fn default() -> Self {
320         FontWeight::Normal
321     }
322 }
323 
324 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
325 #[cfg_attr(feature = "serde", derive(Serialize))]
326 #[strum(serialize_all = "kebab_case")]
327 pub enum TextDecoration {
328     None,
329     Underline,
330 }
331 
332 impl EnumGetAttribute for TextDecoration {}
333 impl Default for TextDecoration {
default() -> Self334     fn default() -> Self {
335         TextDecoration::None
336     }
337 }
338 
339 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
340 #[cfg_attr(feature = "serde", derive(Serialize))]
341 pub enum VerticalAlignment {
342     #[strum(serialize = "baseline")]
343     Baseline,
344     #[strum(serialize = "sup")]
345     Superscript,
346     #[strum(serialize = "sub")]
347     Subscript,
348 }
349 
350 impl EnumGetAttribute for VerticalAlignment {}
351 impl Default for VerticalAlignment {
default() -> Self352     fn default() -> Self {
353         VerticalAlignment::Baseline
354     }
355 }
356 
357 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
358 #[strum(serialize_all = "kebab_case")]
359 pub enum Plural {
360     Contextual,
361     Always,
362     Never,
363 }
364 impl EnumGetAttribute for Plural {}
365 
366 impl Default for Plural {
default() -> Self367     fn default() -> Self {
368         Plural::Contextual
369     }
370 }
371 
372 impl IsIndependent for Cond {
is_independent(&self) -> bool373     fn is_independent(&self) -> bool {
374         match self {
375             Cond::Disambiguate(_) => true,
376             Cond::Position(_) => true,
377             Cond::Locator(_) => true,
378             Cond::Variable(v) => v.is_independent(),
379             Cond::IsNumeric(v) => v.is_independent(),
380             _ => false,
381         }
382     }
383 }
384 
385 #[derive(Debug, Eq, Hash, Clone, PartialEq)]
386 pub enum Cond {
387     IsNumeric(AnyVariable),
388     Variable(AnyVariable),
389     Position(Position),
390     Locator(LocatorType),
391     Disambiguate(bool),
392     Type(CslType),
393     IsUncertainDate(DateVariable),
394     HasYearOnly(DateVariable),
395     HasMonthOrSeason(DateVariable),
396     HasDay(DateVariable),
397     Context(Context),
398     IsPlural(NameVariable),
399     Jurisdiction(SmartString),
400     SubJurisdiction(u32),
401 }
402 
403 #[derive(Debug, Eq, Clone, PartialEq)]
404 pub struct CondSet {
405     pub match_type: Match,
406     pub conds: FnvHashSet<Cond>,
407 }
408 
409 impl From<ConditionParser> for CondSet {
410     #[rustfmt::skip]
411     // Much neater to treat them all the same
412     #[allow(clippy::for_loop_over_option)]
from(cp: ConditionParser) -> Self413     fn from(cp: ConditionParser) -> Self {
414         let mut conds = FnvHashSet::default();
415         for x in cp.position { conds.insert(Cond::Position(x)); }
416         for x in cp.csl_type { conds.insert(Cond::Type(x)); }
417         for x in cp.locator { conds.insert(Cond::Locator(x)); }
418         for x in cp.variable { conds.insert(Cond::Variable(x)); }
419         for x in cp.is_numeric { conds.insert(Cond::IsNumeric(x)); }
420         for x in cp.is_plural { conds.insert(Cond::IsPlural(x)); }
421         for x in cp.context { conds.insert(Cond::Context(x)); }
422         for x in cp.disambiguate { conds.insert(Cond::Disambiguate(x)); }
423         for x in cp.is_uncertain_date { conds.insert(Cond::IsUncertainDate(x)); }
424 
425         // CSL-M
426         for x in cp.has_year_only { conds.insert(Cond::HasYearOnly(x)); }
427         for x in cp.has_month_or_season { conds.insert(Cond::HasMonthOrSeason(x)); }
428         for x in cp.has_day { conds.insert(Cond::HasDay(x)); }
429         for x in cp.jurisdiction { conds.insert(Cond::Jurisdiction(x)); }
430         for x in cp.subjurisdictions { conds.insert(Cond::SubJurisdiction(x)); }
431 
432         CondSet {
433             match_type: cp.match_type,
434             conds
435         }
436     }
437 }
438 
439 /// [spec][]
440 ///
441 /// [spec]: https://docs.citationstyles.org/en/stable/specification.html#choose
442 #[derive(Debug, Eq, Clone, PartialEq)]
443 pub(crate) struct ConditionParser {
444     pub match_type: Match,
445 
446     /// TODO: apparently CSL-M has disambiguate="check-ambiguity-and-backreference" as an
447     /// option here. Frank alone knows what that means.
448     /// https://github.com/Juris-M/citeproc-js/blob/30ceaf50a0ef86517a9a8cd46362e450133c7f91/src/attributes.js#L17-L46
449     pub disambiguate: Option<bool>,
450 
451     /// It doesn't make much sense to test non-numeric variables, but the spec definitely says you
452     /// can do it.
453     pub is_numeric: Vec<AnyVariable>,
454     pub variable: Vec<AnyVariable>,
455     pub position: Vec<Position>,
456     pub csl_type: Vec<CslType>,
457     pub locator: Vec<LocatorType>,
458     pub is_uncertain_date: Vec<DateVariable>,
459 
460     // TODO: do not populate in plain CSL mode
461     pub jurisdiction: Option<SmartString>,
462     pub subjurisdictions: Option<u32>,
463 
464     /// https://citeproc-js.readthedocs.io/en/latest/csl-m/index.html#has-year-only-extension
465     pub has_year_only: Vec<DateVariable>,
466     /// https://citeproc-js.readthedocs.io/en/latest/csl-m/index.html#has-day-extension
467     pub has_day: Vec<DateVariable>,
468     /// https://citeproc-js.readthedocs.io/en/latest/csl-m/index.html#has-to-month-or-season-extension
469     /// Original CSL-M is "has-to-month-or-season" which makes no sense.
470     pub has_month_or_season: Vec<DateVariable>,
471     pub context: Option<Context>,
472 
473     // undocumented CSL-M features
474     // are there are more of these lurking in the citeproc-js codebase?
475 
476     // https://github.com/Juris-M/citeproc-js/blob/30ceaf50a0ef86517a9a8cd46362e450133c7f91/src/attributes.js#L599-L627
477     pub is_plural: Vec<NameVariable>,
478 }
479 
480 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
481 #[strum(serialize_all = "kebab_case")]
482 pub enum Context {
483     Citation,
484     Bibliography,
485 }
486 impl EnumGetAttribute for Context {}
487 
488 impl ConditionParser {
is_empty(&self) -> bool489     pub fn is_empty(&self) -> bool {
490         self.disambiguate.is_none()
491             && self.is_numeric.is_empty()
492             && self.variable.is_empty()
493             && self.position.is_empty()
494             && self.csl_type.is_empty()
495             && self.locator.is_empty()
496             && self.is_uncertain_date.is_empty()
497             && self.has_year_only.is_empty()
498             && self.has_day.is_empty()
499             && self.has_month_or_season.is_empty()
500             && self.jurisdiction.is_none()
501             && self.subjurisdictions.is_none()
502             && self.is_plural.is_empty()
503             && self.context.is_none()
504     }
505 }
506 
507 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Clone, PartialEq, Eq)]
508 #[strum(serialize_all = "kebab_case")]
509 pub enum Match {
510     Any,
511     All,
512     None,
513     /// CSL-M only
514     #[strum(props(csl = "0", cslM = "1"))]
515     Nand,
516 }
517 
518 impl EnumGetAttribute for Match {}
519 impl Default for Match {
default() -> Self520     fn default() -> Self {
521         Match::Any
522     }
523 }
524 
525 #[derive(Debug, Eq, Clone, PartialEq)]
526 // in CSL 1.0.1, conditions.len() == 1
527 pub struct IfThen(pub Conditions, pub Vec<Element>);
528 
529 #[derive(Debug, Eq, Clone, PartialEq)]
530 pub struct Conditions(pub Match, pub Vec<CondSet>);
531 
532 #[derive(Debug, Eq, Clone, PartialEq)]
533 pub struct Else(pub Vec<Element>);
534 
535 #[derive(Debug, Eq, Clone, PartialEq)]
536 pub struct Choose(pub IfThen, pub Vec<IfThen>, pub Else);
537 
538 #[derive(Debug, Default, Eq, Clone, PartialEq)]
539 pub struct Names {
540     // inheritable.
541     pub delimiter: Option<SmartString>,
542 
543     // non-inheritable
544     pub variables: Vec<NameVariable>,
545     pub name: Option<Name>,
546     pub label: Option<NameLabelInput>,
547     pub et_al: Option<NameEtAl>,
548     pub substitute: Option<Substitute>,
549     pub formatting: Option<Formatting>,
550     pub display: Option<DisplayMode>,
551     pub affixes: Option<Affixes>,
552 
553     /// CSL-M: institutions
554     pub with: Option<NameWith>,
555     /// CSL-M: institutions
556     pub institution: Option<Institution>,
557 }
558 
559 /// The available inheritable attributes for cs:name are and, delimiter-precedes-et-al,
560 /// delimiter-precedes-last, et-al-min, et-al-use-first, et-al-use-last, et-al-subsequent-min,
561 /// et-al-subsequent-use-first, initialize, initialize-with, name-as-sort-order and sort-separator.
562 /// The attributes name-form and name-delimiter correspond to the form and delimiter attributes on
563 /// cs:name. Similarly, names-delimiter corresponds to the delimiter attribute on cs:names.
564 
565 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
566 #[strum(serialize_all = "kebab_case")]
567 pub enum NameAnd {
568     Text,
569     Symbol,
570 }
571 
572 impl EnumGetAttribute for NameAnd {}
573 
574 /// It is not entirely clear which attributes `<cs:with>` supports.
575 #[derive(Debug, Eq, Clone, PartialEq, Default)]
576 pub struct NameWith {
577     pub formatting: Option<Formatting>,
578     pub affixes: Option<Affixes>,
579 }
580 
581 #[derive(Debug, Eq, Clone, PartialEq, Default)]
582 pub struct Institution {
583     pub and: Option<NameAnd>,
584     pub delimiter: Option<SmartString>,
585     pub use_first: Option<InstitutionUseFirst>,
586     /// This is different from the `*_use_last` on a Name, which is a boolean to activate `one,
587     /// two,... last`.
588     ///
589     /// Instead, it plucks institution segments from the end in the same way use_first pulls from
590     /// the start.
591     pub use_last: Option<u32>,
592     /// default is false
593     pub reverse_order: bool,
594     pub parts_selector: InstitutionParts,
595     pub institution_parts: Vec<InstitutionPart>,
596     // Not clearly part of the spec, but may be necessary.
597     // pub formatting: Option<Formatting>,
598     // pub affixes: Affixes,
599 
600     // TODO: suppress-min
601 }
602 
603 #[derive(Debug, Eq, Clone, PartialEq, Default)]
604 pub struct InstitutionPart {
605     pub name: InstitutionPartName,
606     pub formatting: Option<Formatting>,
607     pub affixes: Option<Affixes>,
608     // TODO: is this better achieved using initialize-with?
609     pub strip_periods: StripPeriods,
610 }
611 
612 type IfShort = bool;
613 
614 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
615 #[strum(serialize_all = "kebab_case")]
616 pub enum InstitutionPartName {
617     Long(IfShort),
618     Short,
619 }
620 
621 impl EnumGetAttribute for InstitutionPartName {}
622 
623 impl Default for InstitutionPartName {
default() -> Self624     fn default() -> Self {
625         InstitutionPartName::Long(false)
626     }
627 }
628 
629 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
630 #[strum(serialize_all = "kebab_case")]
631 pub enum InstitutionParts {
632     Long,
633     Short,
634     ShortLong,
635     LongShort,
636 }
637 
638 impl EnumGetAttribute for InstitutionParts {}
639 
640 impl Default for InstitutionParts {
default() -> Self641     fn default() -> Self {
642         InstitutionParts::Long
643     }
644 }
645 
646 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
647 #[strum(serialize_all = "kebab_case")]
648 pub enum InstitutionUseFirst {
649     /// Set with `use-first="1"`
650     Normal(u32),
651     /// Set with `substitute-use-first="1"`
652     ///
653     /// The substitute-use-first attribute includes the leading (smallest) subunit if and only if
654     /// no personal names are associated with the organization.
655     Substitute(u32),
656 }
657 
658 #[derive(Debug, Eq, Clone, PartialEq, Hash)]
659 pub struct Name {
660     pub and: Option<NameAnd>,
661     /// Between individual names for the same variable
662     pub delimiter: Option<SmartString>,
663     pub delimiter_precedes_et_al: Option<DelimiterPrecedes>,
664     pub delimiter_precedes_last: Option<DelimiterPrecedes>,
665     pub et_al_min: Option<u32>,
666     pub et_al_use_first: Option<u32>,
667     pub et_al_use_last: Option<bool>, // default is false
668     pub et_al_subsequent_min: Option<u32>,
669     pub et_al_subsequent_use_first: Option<u32>,
670     pub form: Option<NameForm>,
671     pub initialize: Option<bool>, // default is true
672     pub initialize_with: Option<SmartString>,
673     pub name_as_sort_order: Option<NameAsSortOrder>,
674     pub sort_separator: Option<SmartString>,
675     pub formatting: Option<Formatting>,
676     pub affixes: Option<Affixes>,
677     pub name_part_given: Option<NamePart>,
678     pub name_part_family: Option<NamePart>,
679 }
680 
681 impl Default for Name {
default() -> Self682     fn default() -> Self {
683         Name::empty()
684     }
685 }
686 
687 impl Name {
empty() -> Self688     pub fn empty() -> Self {
689         Name {
690             and: None,
691             delimiter: None,
692             delimiter_precedes_et_al: None,
693             delimiter_precedes_last: None,
694             et_al_min: None,
695             et_al_use_first: None,
696             et_al_use_last: None,
697             et_al_subsequent_min: None,
698             et_al_subsequent_use_first: None,
699             form: None,
700             initialize: None,
701             initialize_with: None,
702             name_as_sort_order: None,
703             sort_separator: None,
704             // these four aren't inherited
705             formatting: None,
706             affixes: Default::default(),
707             name_part_given: None,
708             name_part_family: None,
709         }
710     }
711 
712     /// All properties on a Name may be inherited from elsewhere. Therefore while the
713     /// `Default::default()` implementation will give you lots of `None`s, you need to define what
714     /// those Nones should default to absent a parent giving a concrete definition.
715     ///
716     /// This follows how [citeproc-js][defaults] sets the defaults, because this is not specified
717     /// in the spec(s).
718     ///
719     /// [defaults]: https://github.com/Juris-M/citeproc-js/blob/30ceaf50a0ef86517a9a8cd46362e450133c7f91/src/state.js#L103-L121
root_default() -> Self720     pub fn root_default() -> Self {
721         Name {
722             and: None,
723             delimiter: Some(", ".into()),
724             delimiter_precedes_et_al: Some(DelimiterPrecedes::Contextual),
725             delimiter_precedes_last: Some(DelimiterPrecedes::Contextual),
726             et_al_min: None,
727             et_al_use_first: None,
728             et_al_use_last: Some(false),
729             et_al_subsequent_min: None, // must fall back to et_al_min
730             et_al_subsequent_use_first: None, // must fall back to et_al_use_first
731             // https://github.com/Juris-M/citeproc-js/blob/30ceaf50a0ef86517a9a8cd46362e450133c7f91/src/util_names_render.js#L710
732             form: Some(NameForm::Long),
733             initialize: Some(true),
734             // https://github.com/Juris-M/citeproc-js/blob/30ceaf50a0ef86517a9a8cd46362e450133c7f91/src/util_names_render.js#L739
735             initialize_with: None,
736             name_as_sort_order: None,
737             sort_separator: Some(", ".into()),
738             // these four aren't inherited
739             formatting: None,
740             affixes: Default::default(),
741             name_part_given: None,
742             name_part_family: None,
743         }
744     }
745 
746     /// Takes an upstream Name definition, and merges it with a more local one that will
747     /// override any fields set.
748     ///
749     /// Currently, also, it is not possible to override properties that don't accept a
750     /// "none"/"default" option back to their default after setting it on a parent element.
751     /// Like, once you set "name-as-sort-order", you cannot go back to Firstname Lastname.
752     ///
merge(&self, overrider: &Self) -> Self753     pub fn merge(&self, overrider: &Self) -> Self {
754         Name {
755             and: overrider.and.clone().or(self.and),
756             delimiter: overrider
757                 .delimiter
758                 .clone()
759                 .or_else(|| self.delimiter.clone()),
760             delimiter_precedes_et_al: overrider
761                 .delimiter_precedes_et_al
762                 .or(self.delimiter_precedes_et_al),
763             delimiter_precedes_last: overrider
764                 .delimiter_precedes_last
765                 .or(self.delimiter_precedes_last),
766             et_al_min: overrider.et_al_min.or(self.et_al_min),
767             et_al_use_first: overrider.et_al_use_first.or(self.et_al_use_first),
768             et_al_use_last: overrider.et_al_use_last.or(self.et_al_use_last),
769             et_al_subsequent_min: overrider.et_al_subsequent_min.or(self.et_al_subsequent_min),
770             et_al_subsequent_use_first: overrider
771                 .et_al_subsequent_use_first
772                 .or(self.et_al_subsequent_use_first),
773             form: overrider.form.or(self.form),
774             initialize: overrider.initialize.or(self.initialize),
775             initialize_with: overrider
776                 .initialize_with
777                 .clone()
778                 .or_else(|| self.initialize_with.clone()),
779             name_as_sort_order: overrider.name_as_sort_order.or(self.name_as_sort_order),
780             sort_separator: overrider
781                 .sort_separator
782                 .clone()
783                 .or_else(|| self.sort_separator.clone()),
784 
785             // these four aren't inherited
786             formatting: overrider.formatting,
787             affixes: overrider.affixes.clone(),
788             name_part_given: overrider.name_part_given.clone(),
789             name_part_family: overrider.name_part_family.clone(),
790         }
791     }
792 
enable_et_al(&self) -> bool793     pub fn enable_et_al(&self) -> bool {
794         self.et_al_min.is_some() && self.et_al_use_first.is_some()
795     }
796 }
797 #[derive(Debug, Default, Eq, Clone, PartialEq)]
798 pub struct NameLabelInput {
799     pub form: Option<TermFormExtended>,
800     pub formatting: Option<Formatting>,
801     pub plural: Option<Plural>,
802     pub strip_periods: Option<StripPeriods>,
803     pub affixes: Option<Affixes>,
804     pub text_case: Option<TextCase>,
805     pub after_name: bool,
806 }
807 
808 impl NameLabelInput {
empty() -> Self809     pub fn empty() -> Self {
810         Default::default()
811     }
concrete(&self) -> NameLabel812     pub fn concrete(&self) -> NameLabel {
813         NameLabel {
814             form: self.form.unwrap_or_default(),
815             formatting: self.formatting,
816             plural: self.plural.unwrap_or_default(),
817             strip_periods: self.strip_periods.unwrap_or(false),
818             affixes: self.affixes.as_ref().cloned(),
819             text_case: self.text_case.unwrap_or_default(),
820             after_name: self.after_name,
821         }
822     }
merge(&self, other: &NameLabelInput) -> NameLabelInput823     pub fn merge(&self, other: &NameLabelInput) -> NameLabelInput {
824         NameLabelInput {
825             form: other.form.or(self.form),
826             formatting: other.formatting.or(self.formatting),
827             plural: other.plural.or(self.plural),
828             strip_periods: other.strip_periods.or(self.strip_periods),
829             affixes: other
830                 .affixes
831                 .as_ref()
832                 .cloned()
833                 .or_else(|| self.affixes.as_ref().cloned()),
834             text_case: other.text_case.or(self.text_case),
835             after_name: other.after_name,
836         }
837     }
838 }
839 
840 #[derive(Debug, Eq, Clone, PartialEq)]
841 pub struct NameLabel {
842     pub form: TermFormExtended,
843     pub formatting: Option<Formatting>,
844     pub plural: Plural,
845     pub strip_periods: StripPeriods,
846     pub affixes: Option<Affixes>,
847     pub text_case: TextCase,
848     pub after_name: bool,
849 }
850 
851 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
852 pub struct NameEtAl {
853     // TODO: only accept "et-al" or "and others"
854     pub term: String,
855     pub formatting: Option<Formatting>,
856 }
857 
858 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
859 #[strum(serialize_all = "kebab_case")]
860 pub enum DemoteNonDroppingParticle {
861     Never,
862     SortOnly,
863     DisplayAndSort,
864 }
865 impl EnumGetAttribute for DemoteNonDroppingParticle {}
866 
867 impl Default for DemoteNonDroppingParticle {
default() -> Self868     fn default() -> Self {
869         DemoteNonDroppingParticle::DisplayAndSort
870     }
871 }
872 
873 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
874 #[strum(serialize_all = "kebab_case")]
875 pub enum DelimiterPrecedes {
876     Contextual,
877     AfterInvertedName,
878     Always,
879     Never,
880 }
881 
882 impl EnumGetAttribute for DelimiterPrecedes {}
883 impl Default for DelimiterPrecedes {
default() -> Self884     fn default() -> Self {
885         DelimiterPrecedes::Contextual
886     }
887 }
888 
889 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
890 #[strum(serialize_all = "kebab_case")]
891 pub enum NameForm {
892     Long,
893     Short,
894     Count,
895 }
896 impl EnumGetAttribute for NameForm {}
897 
898 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
899 #[strum(serialize_all = "kebab_case")]
900 pub enum NameAsSortOrder {
901     First,
902     All,
903 }
904 impl EnumGetAttribute for NameAsSortOrder {}
905 
906 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
907 #[strum(serialize_all = "kebab_case")]
908 pub enum NamePartName {
909     Given,
910     Family,
911 }
912 impl EnumGetAttribute for NamePartName {}
913 
914 #[derive(Debug, Eq, Clone, PartialEq, Hash)]
915 pub struct NamePart {
916     pub name: NamePartName,
917     pub affixes: Option<Affixes>,
918     pub text_case: TextCase,
919     pub formatting: Option<Formatting>,
920 }
921 
922 #[derive(Debug, Eq, Clone, PartialEq)]
923 pub struct Substitute(pub Vec<Element>);
924 
925 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
926 #[strum(serialize_all = "kebab_case")]
927 pub enum GivenNameDisambiguationRule {
928     AllNames,
929     AllNamesWithInitials,
930     PrimaryName,
931     PrimaryNameWithInitials,
932     ByCite,
933 }
934 
935 impl EnumGetAttribute for GivenNameDisambiguationRule {}
936 
937 impl Default for GivenNameDisambiguationRule {
default() -> Self938     fn default() -> Self {
939         GivenNameDisambiguationRule::ByCite
940     }
941 }
942 
943 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
944 #[strum(serialize_all = "kebab_case")]
945 pub enum Collapse {
946     CitationNumber,
947     Year,
948     YearSuffix,
949     YearSuffixRanged,
950 }
951 impl EnumGetAttribute for Collapse {}
952 
953 #[derive(Debug, Eq, Clone, PartialEq)]
954 pub struct Citation {
955     pub disambiguate_add_names: bool,
956     pub disambiguate_add_givenname: bool,
957     pub givenname_disambiguation_rule: GivenNameDisambiguationRule,
958     pub disambiguate_add_year_suffix: bool,
959     pub layout: Layout,
960     pub name_inheritance: Name,
961     pub names_delimiter: Option<SmartString>,
962     pub near_note_distance: u32,
963     pub sort: Option<Sort>,
964     pub cite_group_delimiter: Option<SmartString>,
965     pub year_suffix_delimiter: Option<SmartString>,
966     pub after_collapse_delimiter: Option<SmartString>,
967     pub collapse: Option<Collapse>,
968 }
969 
970 impl Default for Citation {
default() -> Self971     fn default() -> Self {
972         Citation {
973             disambiguate_add_names: false,
974             disambiguate_add_givenname: false,
975             givenname_disambiguation_rule: Default::default(),
976             disambiguate_add_year_suffix: false,
977             layout: Default::default(),
978             name_inheritance: Default::default(),
979             names_delimiter: None,
980             near_note_distance: 5,
981             sort: None,
982             cite_group_delimiter: None,
983             year_suffix_delimiter: None,
984             after_collapse_delimiter: None,
985             collapse: None,
986         }
987     }
988 }
989 
990 impl Citation {
group_collapsing(&self) -> Option<(&str, Option<Collapse>)>991     pub fn group_collapsing(&self) -> Option<(&str, Option<Collapse>)> {
992         let col = self.collapse;
993         match self.cite_group_delimiter.as_ref() {
994             Some(cgd) => Some((cgd.as_ref(), col)),
995             None => col.map(|c| (", ", Some(c))),
996         }
997     }
998 }
999 
1000 #[derive(Debug, Eq, Clone, PartialEq)]
1001 pub struct Bibliography {
1002     pub sort: Option<Sort>,
1003     pub layout: Layout,
1004     pub hanging_indent: bool, // default is false
1005     pub second_field_align: Option<SecondFieldAlign>,
1006     pub line_spaces: u32,   // >= 1 only. default is 1
1007     pub entry_spacing: u32, // >= 0. default is 1
1008     pub name_inheritance: Name,
1009     pub subsequent_author_substitute: Option<SmartString>,
1010     pub subsequent_author_substitute_rule: SubsequentAuthorSubstituteRule,
1011     pub names_delimiter: Option<SmartString>,
1012 }
1013 
1014 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1015 #[strum(serialize_all = "kebab_case")]
1016 pub enum SecondFieldAlign {
1017     Flush,
1018     Margin,
1019 }
1020 impl EnumGetAttribute for SecondFieldAlign {}
1021 
1022 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1023 #[strum(serialize_all = "kebab_case")]
1024 pub enum SubsequentAuthorSubstituteRule {
1025     CompleteAll,
1026     CompleteEach,
1027     PartialEach,
1028     PartialFirst,
1029 }
1030 impl EnumGetAttribute for SubsequentAuthorSubstituteRule {}
1031 
1032 impl Default for SubsequentAuthorSubstituteRule {
default() -> Self1033     fn default() -> Self {
1034         SubsequentAuthorSubstituteRule::CompleteAll
1035     }
1036 }
1037 
1038 #[derive(Debug, Eq, Clone, PartialEq)]
1039 pub struct Sort {
1040     pub keys: Vec<SortKey>,
1041 }
1042 
1043 #[derive(Debug, Eq, Clone, PartialEq, Hash)]
1044 pub struct SortKey {
1045     pub sort_source: SortSource,
1046     pub names_min: Option<u32>,
1047     pub names_use_first: Option<u32>,
1048     pub names_use_last: Option<bool>,
1049     pub direction: Option<SortDirection>,
1050 }
1051 
1052 impl SortKey {
is_macro(&self) -> bool1053     pub fn is_macro(&self) -> bool {
1054         match self.sort_source {
1055             SortSource::Macro(_) => true,
1056             _ => false,
1057         }
1058     }
macro_named(name: impl Into<SmartString>) -> Self1059     pub fn macro_named(name: impl Into<SmartString>) -> Self {
1060         SortKey {
1061             sort_source: SortSource::Macro(name.into()),
1062             names_min: None,
1063             names_use_first: None,
1064             names_use_last: None,
1065             direction: None,
1066         }
1067     }
1068 }
1069 
1070 /// You must sort on either a variable or a macro
1071 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
1072 pub enum SortSource {
1073     Variable(AnyVariable),
1074     Macro(SmartString),
1075 }
1076 
1077 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1078 #[strum(serialize_all = "kebab_case")]
1079 pub enum SortDirection {
1080     Ascending,
1081     Descending,
1082 }
1083 impl EnumGetAttribute for SortDirection {}
1084 
1085 impl Default for SortDirection {
default() -> Self1086     fn default() -> Self {
1087         SortDirection::Ascending
1088     }
1089 }
1090 
1091 // TODO: Multiple layouts in CSL-M with locale="en es de" etc
1092 #[derive(Default, Debug, Eq, Clone, PartialEq)]
1093 pub struct Layout {
1094     pub affixes: Option<Affixes>,
1095     pub formatting: Option<Formatting>,
1096     // TODO: only allow delimiter inside <citation>
1097     pub delimiter: Option<SmartString>,
1098     pub elements: Vec<Element>,
1099     pub locale: Vec<Lang>,
1100 }
1101 
1102 // Not actually part of a style tree, just a useful place to implement FromNode.
1103 #[derive(Debug, Eq, Clone, PartialEq)]
1104 pub struct MacroMap {
1105     pub name: SmartString,
1106     pub elements: Vec<Element>,
1107 }
1108 
1109 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1110 #[cfg_attr(feature = "serde", derive(Serialize))]
1111 #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
1112 #[strum(serialize_all = "kebab_case")]
1113 pub enum StyleClass {
1114     InText,
1115     Note,
1116 }
1117 
1118 impl EnumGetAttribute for StyleClass {}
1119 impl Default for StyleClass {
default() -> Self1120     fn default() -> Self {
1121         StyleClass::Note
1122     }
1123 }
1124 
1125 #[derive(Debug, Eq, Clone, PartialEq)]
1126 pub struct Style {
1127     pub class: StyleClass,
1128     pub macros: FnvHashMap<SmartString, Vec<Element>>,
1129     pub citation: Citation,
1130     pub bibliography: Option<Bibliography>,
1131     pub info: Info,
1132     pub features: Features,
1133     pub name_inheritance: Name,
1134     pub names_delimiter: Option<SmartString>,
1135     /// `None` is the 'override everything' locale.
1136     pub locale_overrides: FnvHashMap<Option<Lang>, Locale>,
1137     pub default_locale: Option<Lang>,
1138     pub version_req: CslVersionReq,
1139     pub page_range_format: Option<PageRangeFormat>,
1140     pub demote_non_dropping_particle: DemoteNonDroppingParticle,
1141     pub initialize_with_hyphen: bool, // default is true
1142 }
1143 
1144 impl Default for Style {
default() -> Self1145     fn default() -> Self {
1146         Style {
1147             class: Default::default(),
1148             macros: Default::default(),
1149             citation: Default::default(),
1150             features: Default::default(),
1151             bibliography: Default::default(),
1152             info: Default::default(),
1153             name_inheritance: Default::default(),
1154             names_delimiter: None,
1155             locale_overrides: Default::default(),
1156             default_locale: None,
1157             version_req: CslVersionReq::current_csl(),
1158             page_range_format: None,
1159             demote_non_dropping_particle: Default::default(),
1160             initialize_with_hyphen: true,
1161         }
1162     }
1163 }
1164 
1165 impl Style {
name_info_citation(&self) -> (Option<SmartString>, Arc<Name>)1166     pub fn name_info_citation(&self) -> (Option<SmartString>, Arc<Name>) {
1167         let nc = Arc::new(self.name_citation());
1168         let nd = self.names_delimiter.clone();
1169         let citation_nd = self.citation.names_delimiter.clone();
1170         (citation_nd.or(nd), nc)
1171     }
name_info_bibliography(&self) -> (Option<SmartString>, Arc<Name>)1172     pub fn name_info_bibliography(&self) -> (Option<SmartString>, Arc<Name>) {
1173         let nb = Arc::new(self.name_bibliography());
1174         let nd = self.names_delimiter.clone();
1175         let bib_nd = self
1176             .bibliography
1177             .as_ref()
1178             .and_then(|bib| bib.names_delimiter.clone());
1179         (bib_nd.or(nd), nb)
1180     }
name_citation(&self) -> Name1181     pub fn name_citation(&self) -> Name {
1182         let default = Name::root_default();
1183         let root = &self.name_inheritance;
1184         let citation = &self.citation.name_inheritance;
1185         default.merge(root).merge(citation)
1186     }
name_bibliography(&self) -> Name1187     pub fn name_bibliography(&self) -> Name {
1188         let default = Name::root_default();
1189         let root = &self.name_inheritance;
1190         let root = default.merge(root);
1191         if let Some(bib) = &self.bibliography {
1192             root.merge(&bib.name_inheritance)
1193         } else {
1194             root
1195         }
1196     }
1197 }
1198 
1199 #[derive(Debug, Eq, Clone, PartialEq)]
1200 pub struct RangeDelimiter(pub SmartString);
1201 
1202 impl Default for RangeDelimiter {
default() -> Self1203     fn default() -> Self {
1204         RangeDelimiter("\u{2013}".into())
1205     }
1206 }
1207 
1208 impl std::convert::AsRef<str> for RangeDelimiter {
as_ref(&self) -> &str1209     fn as_ref(&self) -> &str {
1210         self.0.as_ref()
1211     }
1212 }
1213 
1214 impl FromStr for RangeDelimiter {
1215     type Err = UnknownAttributeValue;
from_str(s: &str) -> Result<Self, Self::Err>1216     fn from_str(s: &str) -> Result<Self, Self::Err> {
1217         Ok(RangeDelimiter(s.into()))
1218     }
1219 }
1220 
1221 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1222 #[strum(serialize_all = "kebab_case")]
1223 pub enum DateParts {
1224     YearMonthDay,
1225     YearMonth,
1226     Year,
1227 }
1228 
1229 impl EnumGetAttribute for DateParts {}
1230 impl Default for DateParts {
default() -> Self1231     fn default() -> Self {
1232         DateParts::YearMonthDay
1233     }
1234 }
1235 
1236 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1237 #[strum(serialize_all = "kebab_case")]
1238 /// Strictly used for parsing day/month/year
1239 pub(crate) enum DatePartName {
1240     Day,
1241     Month,
1242     Year,
1243 }
1244 impl EnumGetAttribute for DatePartName {}
1245 
1246 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1247 #[strum(serialize_all = "kebab_case")]
1248 pub enum DayForm {
1249     Numeric,
1250     NumericLeadingZeros,
1251     Ordinal,
1252 }
1253 impl EnumGetAttribute for DayForm {}
1254 impl Default for DayForm {
default() -> Self1255     fn default() -> Self {
1256         DayForm::Numeric
1257     }
1258 }
1259 
1260 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1261 #[strum(serialize_all = "kebab_case")]
1262 pub enum MonthForm {
1263     Long,
1264     Short,
1265     Numeric,
1266     NumericLeadingZeros,
1267 }
1268 impl EnumGetAttribute for MonthForm {}
1269 impl Default for MonthForm {
default() -> Self1270     fn default() -> Self {
1271         MonthForm::Long
1272     }
1273 }
1274 
1275 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq)]
1276 #[strum(serialize_all = "kebab_case")]
1277 pub enum YearForm {
1278     Long,
1279     Short,
1280 }
1281 impl EnumGetAttribute for YearForm {}
1282 impl Default for YearForm {
default() -> Self1283     fn default() -> Self {
1284         YearForm::Long
1285     }
1286 }
1287 
1288 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1289 #[strum(serialize_all = "kebab_case")]
1290 pub enum DateForm {
1291     Text,
1292     Numeric,
1293 }
1294 impl EnumGetAttribute for DateForm {}
1295 
1296 #[derive(Debug, Display, Eq, Copy, Clone, PartialEq)]
1297 pub enum DatePartForm {
1298     Day(DayForm),
1299     Month(MonthForm, StripPeriods),
1300     Year(YearForm),
1301 }
1302 
1303 impl DatePartForm {
1304     // For sorting date parts when rendering a sort string for a date through a macro, i.e. with
1305     // filtered parts
num(&self) -> i321306     fn num(&self) -> i32 {
1307         match self {
1308             DatePartForm::Year(..) => 0,
1309             DatePartForm::Month(..) => 1,
1310             DatePartForm::Day(_) => 2,
1311         }
1312     }
1313 }
1314 
1315 use std::cmp::Ordering;
1316 impl Ord for DatePartForm {
cmp(&self, other: &Self) -> Ordering1317     fn cmp(&self, other: &Self) -> Ordering {
1318         self.num().cmp(&other.num())
1319     }
1320 }
1321 impl PartialOrd for DatePartForm {
partial_cmp(&self, other: &Self) -> Option<Ordering>1322     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1323         Some(self.cmp(other))
1324     }
1325 }
1326 
1327 #[derive(Debug, Default, Eq, Clone, PartialEq)]
1328 pub struct DatePart {
1329     pub form: DatePartForm,
1330     pub affixes: Option<Affixes>,
1331     pub formatting: Option<Formatting>,
1332     pub text_case: Option<TextCase>,
1333     pub range_delimiter: Option<RangeDelimiter>,
1334 }
1335 
1336 // Only for DatePart::default()
1337 impl Default for DatePartForm {
default() -> Self1338     fn default() -> Self {
1339         DatePartForm::Year(YearForm::Long)
1340     }
1341 }
1342 
1343 /// A date element that fully defines its own output.
1344 /// It is 'independent' of any localization.
1345 #[derive(Debug, Eq, Clone, PartialEq)]
1346 pub struct IndependentDate {
1347     pub variable: DateVariable,
1348     // TODO: limit each <date-part name="XXX"> to one per?
1349     pub date_parts: Vec<DatePart>,
1350     pub delimiter: Option<SmartString>,
1351     pub affixes: Option<Affixes>,
1352     pub formatting: Option<Formatting>,
1353     pub display: Option<DisplayMode>,
1354     pub text_case: TextCase,
1355 }
1356 
1357 /// A date element in the main body of a style that refers to a `LocaleDate`
1358 #[derive(Debug, Eq, Clone, PartialEq)]
1359 pub struct LocalizedDate {
1360     pub variable: DateVariable,
1361     pub parts_selector: DateParts,
1362     pub date_parts: Vec<DatePart>,
1363     pub form: DateForm,
1364     pub affixes: Option<Affixes>,
1365     pub formatting: Option<Formatting>,
1366     pub display: Option<DisplayMode>,
1367     pub text_case: TextCase,
1368 }
1369 
1370 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1371 #[strum(serialize_all = "kebab_case")]
1372 pub enum Position {
1373     First,
1374     Ibid,
1375     IbidWithLocator,
1376     Subsequent,
1377     NearNote,
1378 
1379     // Not supported as a condition check, but this means both ibid and near, which is usually the
1380     // case for an ibid, except when near-note-distance="0" and the ibid refers to a previous cluster.
1381     #[strum(props(csl = "0", cslM = "0"))]
1382     IbidNear,
1383     #[strum(props(csl = "0", cslM = "0"))]
1384     IbidWithLocatorNear,
1385 
1386     /// CSL-M only
1387     ///
1388     /// It [would
1389     /// appear](https://github.com/Juris-M/citeproc-js/blob/30ceaf50a0ef86517a9a8cd46362e450133c7f91/src/attributes.js#L165-L172)
1390     /// this means `subsequent && NOT near-note`, but it is not defined in any specification.
1391     #[strum(props(csl = "0", cslM = "1"))]
1392     FarNote,
1393 }
1394 
1395 impl EnumGetAttribute for Position {}
1396 impl Position {
1397     /// > "Whenever position=”ibid-with-locator” tests true, position=”ibid” also tests true.
1398     /// And whenever position=”ibid” or position=”near-note” test true, position=”subsequent”
1399     /// also tests true."
1400     ///
1401     /// [Spec](http://docs.citationstyles.org/en/stable/specification.html#choose)
matches(self, in_cond: Self) -> bool1402     pub fn matches(self, in_cond: Self) -> bool {
1403         use self::Position::*;
1404         match (self, in_cond) {
1405             (IbidNear, Ibid) => true,
1406             (IbidNear, NearNote) => true,
1407             (IbidNear, Subsequent) => true,
1408             (IbidWithLocatorNear, IbidWithLocator) => true,
1409             (IbidWithLocatorNear, Ibid) => true,
1410             (IbidWithLocatorNear, NearNote) => true,
1411             (IbidWithLocatorNear, Subsequent) => true,
1412             (IbidWithLocator, Ibid) => true,
1413             (IbidWithLocator, Subsequent) => true,
1414             (Ibid, Subsequent) => true,
1415             (FarNote, Subsequent) => true,
1416             (NearNote, Subsequent) => true,
1417             (x, y) => x == y,
1418         }
1419     }
1420 }
1421 
1422 /// [Spec](https://docs.citationstyles.org/en/stable/specification.html#appendix-v-page-range-formats)
1423 #[derive(AsRefStr, EnumProperty, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1424 #[strum(serialize_all = "kebab_case")]
1425 pub enum PageRangeFormat {
1426     Chicago,
1427     Expanded,
1428     Minimal,
1429     MinimalTwo,
1430 }
1431 impl EnumGetAttribute for PageRangeFormat {}
1432 
1433 #[derive(AsRefStr, EnumProperty, EnumIter, EnumString, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1434 #[strum(serialize_all = "kebab_case")]
1435 pub enum CslType {
1436     Article,
1437     ArticleMagazine,
1438     ArticleNewspaper,
1439     ArticleJournal,
1440     Bill,
1441     Book,
1442     Broadcast,
1443     Chapter,
1444     Dataset,
1445     Entry,
1446     EntryDictionary,
1447     EntryEncyclopedia,
1448     Figure,
1449     Graphic,
1450     Interview,
1451     Legislation,
1452     #[strum(serialize = "legal_case")]
1453     LegalCase,
1454     Manuscript,
1455     Map,
1456     #[strum(serialize = "motion_picture")]
1457     MotionPicture,
1458     #[strum(serialize = "musical_score")]
1459     MusicalScore,
1460     Pamphlet,
1461     PaperConference,
1462     Patent,
1463     Post,
1464     PostWeblog,
1465     #[strum(serialize = "personal_communication")]
1466     PersonalCommunication,
1467     Report,
1468     Review,
1469     ReviewBook,
1470     Song,
1471     Speech,
1472     Thesis,
1473     Treaty,
1474     Webpage,
1475 
1476     /// CSL-M only
1477     #[strum(props(csl = "0", cslM = "1"))]
1478     Classic,
1479     /// CSL-M only
1480     #[strum(props(csl = "0", cslM = "1"))]
1481     Gazette,
1482     /// CSL-M only
1483     #[strum(props(csl = "0", cslM = "1"))]
1484     Hearing,
1485     /// CSL-M only
1486     #[strum(props(csl = "0", cslM = "1"))]
1487     Regulation,
1488     /// CSL-M only
1489     #[strum(props(csl = "0", cslM = "1"))]
1490     Video,
1491 }
1492 impl EnumGetAttribute for CslType {}
1493