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