├── .github ├── FUNDING.yml ├── .well-known │ └── funding-manifest-urls └── workflows │ ├── archive.yml │ └── ci.yml ├── .gitattributes ├── archive ├── locales │ ├── ar.cbor │ ├── eu.cbor │ ├── la.cbor │ ├── af-ZA.cbor │ ├── bg-BG.cbor │ ├── ca-AD.cbor │ ├── cs-CZ.cbor │ ├── cy-GB.cbor │ ├── da-DK.cbor │ ├── de-AT.cbor │ ├── de-CH.cbor │ ├── de-DE.cbor │ ├── el-GR.cbor │ ├── en-GB.cbor │ ├── en-US.cbor │ ├── es-CL.cbor │ ├── es-ES.cbor │ ├── es-MX.cbor │ ├── et-EE.cbor │ ├── fa-IR.cbor │ ├── fi-FI.cbor │ ├── fr-CA.cbor │ ├── fr-FR.cbor │ ├── gl-ES.cbor │ ├── he-IL.cbor │ ├── hi-IN.cbor │ ├── hr-HR.cbor │ ├── hu-HU.cbor │ ├── id-ID.cbor │ ├── is-IS.cbor │ ├── it-IT.cbor │ ├── ja-JP.cbor │ ├── km-KH.cbor │ ├── ko-KR.cbor │ ├── lt-LT.cbor │ ├── lv-LV.cbor │ ├── mn-MN.cbor │ ├── ms-MY.cbor │ ├── nb-NO.cbor │ ├── nl-NL.cbor │ ├── nn-NO.cbor │ ├── pa-PK.cbor │ ├── pl-PL.cbor │ ├── pt-BR.cbor │ ├── pt-PT.cbor │ ├── ro-RO.cbor │ ├── ru-RU.cbor │ ├── sk-SK.cbor │ ├── sl-SI.cbor │ ├── sr-RS.cbor │ ├── sv-SE.cbor │ ├── th-TH.cbor │ ├── tr-TR.cbor │ ├── uk-UA.cbor │ ├── vi-VN.cbor │ ├── zh-CN.cbor │ ├── zh-TW.cbor │ ├── bal-PK.cbor │ ├── brh-PK.cbor │ ├── lij-IT.cbor │ ├── sr-Cyrl-RS.cbor │ └── sr-Latn-RS.cbor └── styles │ ├── apa.cbor │ ├── bmj.cbor │ ├── cell.cbor │ ├── ieee.cbor │ ├── nature.cbor │ ├── plos.cbor │ ├── sist02.cbor │ ├── frontiers.cbor │ ├── the-lancet.cbor │ ├── vancouver.cbor │ ├── alphanumeric.cbor │ ├── spie-journals.cbor │ ├── thieme-german.cbor │ ├── annual-reviews.cbor │ ├── biomed-central.cbor │ ├── current-opinion.cbor │ ├── deutsche-sprache.cbor │ ├── elsevier-harvard.cbor │ ├── future-medicine.cbor │ ├── karger-journals.cbor │ ├── pensoft-journals.cbor │ ├── sage-vancouver.cbor │ ├── trends-journals.cbor │ ├── angewandte-chemie.cbor │ ├── elsevier-vancouver.cbor │ ├── iso690-numeric-en.cbor │ ├── chicago-author-date.cbor │ ├── elsevier-with-titles.cbor │ ├── future-science-group.cbor │ ├── iso690-author-date-en.cbor │ ├── turabian-author-date.cbor │ ├── vancouver-superscript.cbor │ ├── copernicus-publications.cbor │ ├── harvard-cite-them-right.cbor │ ├── springer-basic-brackets.cbor │ ├── american-chemical-society.cbor │ ├── american-geophysical-union.cbor │ ├── american-physics-society.cbor │ ├── annual-reviews-author-date.cbor │ ├── bristol-university-press.cbor │ ├── chicago-note-bibliography.cbor │ ├── chicago-notes-bibliography.cbor │ ├── gost-r-7-0-5-2008-numeric.cbor │ ├── mary-ann-liebert-vancouver.cbor │ ├── royal-society-of-chemistry.cbor │ ├── springer-basic-author-date.cbor │ ├── springer-mathphys-brackets.cbor │ ├── american-medical-association.cbor │ ├── chinese-gb7714-2005-numeric.cbor │ ├── institute-of-physics-numeric.cbor │ ├── modern-language-association.cbor │ ├── springer-vancouver-brackets.cbor │ ├── american-institute-of-physics.cbor │ ├── american-meteorological-society.cbor │ ├── american-physiological-society.cbor │ ├── chicago-fullnote-bibliography.cbor │ ├── springer-humanities-author-date.cbor │ ├── springer-socpsych-author-date.cbor │ ├── american-society-for-microbiology.cbor │ ├── american-sociological-association.cbor │ ├── chicago-author-date-17th-edition.cbor │ ├── american-anthropological-association.cbor │ ├── american-society-of-civil-engineers.cbor │ ├── association-for-computing-machinery.cbor │ ├── chicago-shortened-notes-bibliography.cbor │ ├── council-of-science-editors-brackets.cbor │ ├── american-political-science-association.cbor │ ├── council-of-science-editors-author-date.cbor │ ├── deutsche-gesellschaft-fur-psychologie.cbor │ ├── modern-humanities-research-association.cbor │ ├── taylor-and-francis-chicago-author-date.cbor │ ├── american-society-of-mechanical-engineers.cbor │ ├── associacao-brasileira-de-normas-tecnicas.cbor │ ├── modern-language-association-8th-edition.cbor │ ├── china-national-standard-gb-t-7714-2015-note.cbor │ ├── springer-lecture-notes-in-computer-science.cbor │ ├── turabian-fullnote-bibliography-8th-edition.cbor │ ├── china-national-standard-gb-t-7714-2015-numeric.cbor │ ├── modern-humanities-research-association-notes.cbor │ ├── multidisciplinary-digital-publishing-institute.cbor │ ├── springer-fachzeitschriften-medizin-psychologie.cbor │ ├── the-institution-of-engineering-and-technology.cbor │ ├── taylor-and-francis-national-library-of-medicine.cbor │ ├── american-institute-of-aeronautics-and-astronautics.cbor │ ├── china-national-standard-gb-t-7714-2015-author-date.cbor │ └── chicago-notes-bibliography-subsequent-author-title-17th-edition.cbor ├── rustfmt.toml ├── .gitignore ├── tests ├── data │ ├── lotr.bib │ ├── art-history.csl │ └── basic.yml ├── local │ ├── page_NumberPage.txt │ ├── date_DateBCAdjusted.txt │ ├── date_LocalizedDateDelimiters.txt │ ├── page_NumberPageSort.txt │ ├── page_NumberPageMacroSort.txt │ ├── position_IbidWithLocatorIsIbid.txt │ ├── position_IbidIsNotIbidWithLocator.txt │ ├── collapse_CitationNumberRangesSort.txt │ ├── choose_UseAncestorsDelimiter.txt │ ├── name_PartInheritsFormatting.txt │ ├── date_NegativeDateSort.txt │ ├── date_NegativeDateSortViaMacro.txt │ ├── date_NegativeDateSortViaMacroOnYearMonthOnly.txt │ └── collapse_CitationNumberRangesSeparated.txt └── common │ └── mod.rs ├── NOTICE ├── LICENSE-MIT ├── styles └── alphanumeric.csl ├── Cargo.toml ├── src ├── lang │ ├── name.rs │ └── en.rs ├── io.rs ├── csl │ ├── citation_label.rs │ └── sort.rs ├── util.rs ├── selectors │ ├── mod.rs │ └── parser.rs └── types │ └── page.rs ├── docs └── selectors.md ├── LICENSE-APACHE ├── CHANGELOG.md └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [typst] 2 | -------------------------------------------------------------------------------- /.github/.well-known/funding-manifest-urls: -------------------------------------------------------------------------------- 1 | https://typst.app/funding.json 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.cbor binary 2 | 3 | *.xml text eol=lf 4 | *.csl text eol=lf 5 | -------------------------------------------------------------------------------- /archive/locales/ar.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/ar.cbor -------------------------------------------------------------------------------- /archive/locales/eu.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/eu.cbor -------------------------------------------------------------------------------- /archive/locales/la.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/la.cbor -------------------------------------------------------------------------------- /archive/styles/apa.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/apa.cbor -------------------------------------------------------------------------------- /archive/styles/bmj.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/bmj.cbor -------------------------------------------------------------------------------- /archive/locales/af-ZA.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/af-ZA.cbor -------------------------------------------------------------------------------- /archive/locales/bg-BG.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/bg-BG.cbor -------------------------------------------------------------------------------- /archive/locales/ca-AD.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/ca-AD.cbor -------------------------------------------------------------------------------- /archive/locales/cs-CZ.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/cs-CZ.cbor -------------------------------------------------------------------------------- /archive/locales/cy-GB.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/cy-GB.cbor -------------------------------------------------------------------------------- /archive/locales/da-DK.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/da-DK.cbor -------------------------------------------------------------------------------- /archive/locales/de-AT.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/de-AT.cbor -------------------------------------------------------------------------------- /archive/locales/de-CH.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/de-CH.cbor -------------------------------------------------------------------------------- /archive/locales/de-DE.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/de-DE.cbor -------------------------------------------------------------------------------- /archive/locales/el-GR.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/el-GR.cbor -------------------------------------------------------------------------------- /archive/locales/en-GB.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/en-GB.cbor -------------------------------------------------------------------------------- /archive/locales/en-US.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/en-US.cbor -------------------------------------------------------------------------------- /archive/locales/es-CL.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/es-CL.cbor -------------------------------------------------------------------------------- /archive/locales/es-ES.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/es-ES.cbor -------------------------------------------------------------------------------- /archive/locales/es-MX.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/es-MX.cbor -------------------------------------------------------------------------------- /archive/locales/et-EE.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/et-EE.cbor -------------------------------------------------------------------------------- /archive/locales/fa-IR.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/fa-IR.cbor -------------------------------------------------------------------------------- /archive/locales/fi-FI.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/fi-FI.cbor -------------------------------------------------------------------------------- /archive/locales/fr-CA.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/fr-CA.cbor -------------------------------------------------------------------------------- /archive/locales/fr-FR.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/fr-FR.cbor -------------------------------------------------------------------------------- /archive/locales/gl-ES.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/gl-ES.cbor -------------------------------------------------------------------------------- /archive/locales/he-IL.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/he-IL.cbor -------------------------------------------------------------------------------- /archive/locales/hi-IN.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/hi-IN.cbor -------------------------------------------------------------------------------- /archive/locales/hr-HR.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/hr-HR.cbor -------------------------------------------------------------------------------- /archive/locales/hu-HU.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/hu-HU.cbor -------------------------------------------------------------------------------- /archive/locales/id-ID.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/id-ID.cbor -------------------------------------------------------------------------------- /archive/locales/is-IS.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/is-IS.cbor -------------------------------------------------------------------------------- /archive/locales/it-IT.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/it-IT.cbor -------------------------------------------------------------------------------- /archive/locales/ja-JP.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/ja-JP.cbor -------------------------------------------------------------------------------- /archive/locales/km-KH.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/km-KH.cbor -------------------------------------------------------------------------------- /archive/locales/ko-KR.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/ko-KR.cbor -------------------------------------------------------------------------------- /archive/locales/lt-LT.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/lt-LT.cbor -------------------------------------------------------------------------------- /archive/locales/lv-LV.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/lv-LV.cbor -------------------------------------------------------------------------------- /archive/locales/mn-MN.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/mn-MN.cbor -------------------------------------------------------------------------------- /archive/locales/ms-MY.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/ms-MY.cbor -------------------------------------------------------------------------------- /archive/locales/nb-NO.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/nb-NO.cbor -------------------------------------------------------------------------------- /archive/locales/nl-NL.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/nl-NL.cbor -------------------------------------------------------------------------------- /archive/locales/nn-NO.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/nn-NO.cbor -------------------------------------------------------------------------------- /archive/locales/pa-PK.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/pa-PK.cbor -------------------------------------------------------------------------------- /archive/locales/pl-PL.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/pl-PL.cbor -------------------------------------------------------------------------------- /archive/locales/pt-BR.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/pt-BR.cbor -------------------------------------------------------------------------------- /archive/locales/pt-PT.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/pt-PT.cbor -------------------------------------------------------------------------------- /archive/locales/ro-RO.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/ro-RO.cbor -------------------------------------------------------------------------------- /archive/locales/ru-RU.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/ru-RU.cbor -------------------------------------------------------------------------------- /archive/locales/sk-SK.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/sk-SK.cbor -------------------------------------------------------------------------------- /archive/locales/sl-SI.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/sl-SI.cbor -------------------------------------------------------------------------------- /archive/locales/sr-RS.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/sr-RS.cbor -------------------------------------------------------------------------------- /archive/locales/sv-SE.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/sv-SE.cbor -------------------------------------------------------------------------------- /archive/locales/th-TH.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/th-TH.cbor -------------------------------------------------------------------------------- /archive/locales/tr-TR.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/tr-TR.cbor -------------------------------------------------------------------------------- /archive/locales/uk-UA.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/uk-UA.cbor -------------------------------------------------------------------------------- /archive/locales/vi-VN.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/vi-VN.cbor -------------------------------------------------------------------------------- /archive/locales/zh-CN.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/zh-CN.cbor -------------------------------------------------------------------------------- /archive/locales/zh-TW.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/zh-TW.cbor -------------------------------------------------------------------------------- /archive/styles/cell.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/cell.cbor -------------------------------------------------------------------------------- /archive/styles/ieee.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/ieee.cbor -------------------------------------------------------------------------------- /archive/styles/nature.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/nature.cbor -------------------------------------------------------------------------------- /archive/styles/plos.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/plos.cbor -------------------------------------------------------------------------------- /archive/styles/sist02.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/sist02.cbor -------------------------------------------------------------------------------- /archive/locales/bal-PK.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/bal-PK.cbor -------------------------------------------------------------------------------- /archive/locales/brh-PK.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/brh-PK.cbor -------------------------------------------------------------------------------- /archive/locales/lij-IT.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/lij-IT.cbor -------------------------------------------------------------------------------- /archive/locales/sr-Cyrl-RS.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/sr-Cyrl-RS.cbor -------------------------------------------------------------------------------- /archive/locales/sr-Latn-RS.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/locales/sr-Latn-RS.cbor -------------------------------------------------------------------------------- /archive/styles/frontiers.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/frontiers.cbor -------------------------------------------------------------------------------- /archive/styles/the-lancet.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/the-lancet.cbor -------------------------------------------------------------------------------- /archive/styles/vancouver.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/vancouver.cbor -------------------------------------------------------------------------------- /archive/styles/alphanumeric.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/alphanumeric.cbor -------------------------------------------------------------------------------- /archive/styles/spie-journals.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/spie-journals.cbor -------------------------------------------------------------------------------- /archive/styles/thieme-german.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/thieme-german.cbor -------------------------------------------------------------------------------- /archive/styles/annual-reviews.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/annual-reviews.cbor -------------------------------------------------------------------------------- /archive/styles/biomed-central.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/biomed-central.cbor -------------------------------------------------------------------------------- /archive/styles/current-opinion.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/current-opinion.cbor -------------------------------------------------------------------------------- /archive/styles/deutsche-sprache.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/deutsche-sprache.cbor -------------------------------------------------------------------------------- /archive/styles/elsevier-harvard.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/elsevier-harvard.cbor -------------------------------------------------------------------------------- /archive/styles/future-medicine.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/future-medicine.cbor -------------------------------------------------------------------------------- /archive/styles/karger-journals.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/karger-journals.cbor -------------------------------------------------------------------------------- /archive/styles/pensoft-journals.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/pensoft-journals.cbor -------------------------------------------------------------------------------- /archive/styles/sage-vancouver.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/sage-vancouver.cbor -------------------------------------------------------------------------------- /archive/styles/trends-journals.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/trends-journals.cbor -------------------------------------------------------------------------------- /archive/styles/angewandte-chemie.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/angewandte-chemie.cbor -------------------------------------------------------------------------------- /archive/styles/elsevier-vancouver.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/elsevier-vancouver.cbor -------------------------------------------------------------------------------- /archive/styles/iso690-numeric-en.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/iso690-numeric-en.cbor -------------------------------------------------------------------------------- /archive/styles/chicago-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chicago-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/elsevier-with-titles.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/elsevier-with-titles.cbor -------------------------------------------------------------------------------- /archive/styles/future-science-group.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/future-science-group.cbor -------------------------------------------------------------------------------- /archive/styles/iso690-author-date-en.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/iso690-author-date-en.cbor -------------------------------------------------------------------------------- /archive/styles/turabian-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/turabian-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/vancouver-superscript.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/vancouver-superscript.cbor -------------------------------------------------------------------------------- /archive/styles/copernicus-publications.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/copernicus-publications.cbor -------------------------------------------------------------------------------- /archive/styles/harvard-cite-them-right.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/harvard-cite-them-right.cbor -------------------------------------------------------------------------------- /archive/styles/springer-basic-brackets.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-basic-brackets.cbor -------------------------------------------------------------------------------- /archive/styles/american-chemical-society.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-chemical-society.cbor -------------------------------------------------------------------------------- /archive/styles/american-geophysical-union.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-geophysical-union.cbor -------------------------------------------------------------------------------- /archive/styles/american-physics-society.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-physics-society.cbor -------------------------------------------------------------------------------- /archive/styles/annual-reviews-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/annual-reviews-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/bristol-university-press.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/bristol-university-press.cbor -------------------------------------------------------------------------------- /archive/styles/chicago-note-bibliography.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chicago-note-bibliography.cbor -------------------------------------------------------------------------------- /archive/styles/chicago-notes-bibliography.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chicago-notes-bibliography.cbor -------------------------------------------------------------------------------- /archive/styles/gost-r-7-0-5-2008-numeric.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/gost-r-7-0-5-2008-numeric.cbor -------------------------------------------------------------------------------- /archive/styles/mary-ann-liebert-vancouver.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/mary-ann-liebert-vancouver.cbor -------------------------------------------------------------------------------- /archive/styles/royal-society-of-chemistry.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/royal-society-of-chemistry.cbor -------------------------------------------------------------------------------- /archive/styles/springer-basic-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-basic-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/springer-mathphys-brackets.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-mathphys-brackets.cbor -------------------------------------------------------------------------------- /archive/styles/american-medical-association.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-medical-association.cbor -------------------------------------------------------------------------------- /archive/styles/chinese-gb7714-2005-numeric.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chinese-gb7714-2005-numeric.cbor -------------------------------------------------------------------------------- /archive/styles/institute-of-physics-numeric.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/institute-of-physics-numeric.cbor -------------------------------------------------------------------------------- /archive/styles/modern-language-association.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/modern-language-association.cbor -------------------------------------------------------------------------------- /archive/styles/springer-vancouver-brackets.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-vancouver-brackets.cbor -------------------------------------------------------------------------------- /archive/styles/american-institute-of-physics.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-institute-of-physics.cbor -------------------------------------------------------------------------------- /archive/styles/american-meteorological-society.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-meteorological-society.cbor -------------------------------------------------------------------------------- /archive/styles/american-physiological-society.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-physiological-society.cbor -------------------------------------------------------------------------------- /archive/styles/chicago-fullnote-bibliography.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chicago-fullnote-bibliography.cbor -------------------------------------------------------------------------------- /archive/styles/springer-humanities-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-humanities-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/springer-socpsych-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-socpsych-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/american-society-for-microbiology.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-society-for-microbiology.cbor -------------------------------------------------------------------------------- /archive/styles/american-sociological-association.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-sociological-association.cbor -------------------------------------------------------------------------------- /archive/styles/chicago-author-date-17th-edition.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chicago-author-date-17th-edition.cbor -------------------------------------------------------------------------------- /archive/styles/american-anthropological-association.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-anthropological-association.cbor -------------------------------------------------------------------------------- /archive/styles/american-society-of-civil-engineers.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-society-of-civil-engineers.cbor -------------------------------------------------------------------------------- /archive/styles/association-for-computing-machinery.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/association-for-computing-machinery.cbor -------------------------------------------------------------------------------- /archive/styles/chicago-shortened-notes-bibliography.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chicago-shortened-notes-bibliography.cbor -------------------------------------------------------------------------------- /archive/styles/council-of-science-editors-brackets.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/council-of-science-editors-brackets.cbor -------------------------------------------------------------------------------- /archive/styles/american-political-science-association.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-political-science-association.cbor -------------------------------------------------------------------------------- /archive/styles/council-of-science-editors-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/council-of-science-editors-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/deutsche-gesellschaft-fur-psychologie.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/deutsche-gesellschaft-fur-psychologie.cbor -------------------------------------------------------------------------------- /archive/styles/modern-humanities-research-association.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/modern-humanities-research-association.cbor -------------------------------------------------------------------------------- /archive/styles/taylor-and-francis-chicago-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/taylor-and-francis-chicago-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/american-society-of-mechanical-engineers.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-society-of-mechanical-engineers.cbor -------------------------------------------------------------------------------- /archive/styles/associacao-brasileira-de-normas-tecnicas.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/associacao-brasileira-de-normas-tecnicas.cbor -------------------------------------------------------------------------------- /archive/styles/modern-language-association-8th-edition.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/modern-language-association-8th-edition.cbor -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_small_heuristics = "Max" 2 | max_width = 90 3 | chain_width = 70 4 | struct_lit_width = 50 5 | use_field_init_shorthand = true 6 | merge_derives = false 7 | -------------------------------------------------------------------------------- /archive/styles/china-national-standard-gb-t-7714-2015-note.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/china-national-standard-gb-t-7714-2015-note.cbor -------------------------------------------------------------------------------- /archive/styles/springer-lecture-notes-in-computer-science.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-lecture-notes-in-computer-science.cbor -------------------------------------------------------------------------------- /archive/styles/turabian-fullnote-bibliography-8th-edition.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/turabian-fullnote-bibliography-8th-edition.cbor -------------------------------------------------------------------------------- /archive/styles/china-national-standard-gb-t-7714-2015-numeric.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/china-national-standard-gb-t-7714-2015-numeric.cbor -------------------------------------------------------------------------------- /archive/styles/modern-humanities-research-association-notes.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/modern-humanities-research-association-notes.cbor -------------------------------------------------------------------------------- /archive/styles/multidisciplinary-digital-publishing-institute.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/multidisciplinary-digital-publishing-institute.cbor -------------------------------------------------------------------------------- /archive/styles/springer-fachzeitschriften-medizin-psychologie.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/springer-fachzeitschriften-medizin-psychologie.cbor -------------------------------------------------------------------------------- /archive/styles/the-institution-of-engineering-and-technology.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/the-institution-of-engineering-and-technology.cbor -------------------------------------------------------------------------------- /archive/styles/taylor-and-francis-national-library-of-medicine.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/taylor-and-francis-national-library-of-medicine.cbor -------------------------------------------------------------------------------- /archive/styles/american-institute-of-aeronautics-and-astronautics.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/american-institute-of-aeronautics-and-astronautics.cbor -------------------------------------------------------------------------------- /archive/styles/china-national-standard-gb-t-7714-2015-author-date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/china-national-standard-gb-t-7714-2015-author-date.cbor -------------------------------------------------------------------------------- /archive/styles/chicago-notes-bibliography-subsequent-author-title-17th-edition.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typst/hayagriva/HEAD/archive/styles/chicago-notes-bibliography-subsequent-author-title-17th-edition.cbor -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VS Code 2 | .vscode 3 | 4 | # Trying stuff locally 5 | /_studies 6 | 7 | # Rust 8 | /target 9 | /main/target 10 | Cargo.lock 11 | **/*.rs.bk 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # Zed editor 17 | .zed 18 | -------------------------------------------------------------------------------- /tests/data/lotr.bib: -------------------------------------------------------------------------------- 1 | @book{tolkien54, 2 | maintitle = {The Lord of the Rings}, 3 | title = {The Fellowship of the Ring}, 4 | author = {J. R. R. Tolkien}, 5 | date = {1954-07-29}, 6 | publisher = {Allen & Unwin}, 7 | location = {London}, 8 | volume = {1}, 9 | } 10 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Licenses for third party components used by this project can be found below. 2 | 3 | ================================================================================ 4 | The Creative Commons BY-SA 3.0 DEED License applies to: 5 | * The CSL styles found in `tests/data/*` 6 | * The CSL styles and locales found in `archive/` 7 | 8 | https://creativecommons.org/licenses/by-sa/3.0/ 9 | -------------------------------------------------------------------------------- /.github/workflows/archive.yml: -------------------------------------------------------------------------------- 1 | name: Weekly archive up-to-date check 2 | on: 3 | schedule: 4 | - cron: 00 3 * * 1 5 | workflow_dispatch: 6 | release: 7 | # Run on draft and final releases 8 | types: [published] 9 | 10 | env: 11 | RUSTFLAGS: "-Dwarnings" 12 | RUSTDOCFLAGS: "-Dwarnings" 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: dtolnay/rust-toolchain@stable 20 | - name: Check out CSL styles 21 | run: | 22 | cd .. 23 | git clone --depth 1 https://github.com/citation-style-language/styles 24 | - name: Check if archives are up-to-date 25 | run: cargo test --test archiver -- --ignored 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous integration 2 | on: [push, pull_request] 3 | 4 | env: 5 | RUSTFLAGS: "-Dwarnings" 6 | RUSTDOCFLAGS: "-Dwarnings" 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: dtolnay/rust-toolchain@stable 14 | - run: cargo build 15 | - run: cargo test --features csl-json 16 | 17 | checks: 18 | name: Check clippy, formatting, and documentation 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: dtolnay/rust-toolchain@1.91.0 23 | with: 24 | components: clippy, rustfmt 25 | - uses: Swatinem/rust-cache@v2 26 | - run: cargo clippy --workspace --all-targets --all-features 27 | - run: cargo fmt --check --all 28 | - run: cargo doc --workspace --no-deps 29 | -------------------------------------------------------------------------------- /tests/local/page_NumberPage.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | 7 | >>===== RESULT =====>> 8 | 22–45 9 | <<===== RESULT =====<< 10 | 11 | 12 | >>===== CSL =====>> 13 | 28 | <<===== CSL =====<< 29 | 30 | 31 | >>===== INPUT =====>> 32 | [ 33 | { 34 | "id": "ITEM-1", 35 | "page": "22-45", 36 | "title": "His Anonymous Life", 37 | "type": "book" 38 | } 39 | ] 40 | <<===== INPUT =====<< 41 | 42 | 43 | >>===== VERSION =====>> 44 | 1.0 45 | <<===== VERSION =====<< 46 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /styles/alphanumeric.csl: -------------------------------------------------------------------------------- 1 | 2 | 34 | -------------------------------------------------------------------------------- /tests/local/date_DateBCAdjusted.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | 7 | 8 | >>===== RESULT =====>> 9 | (251 BC) 10 | <<===== RESULT =====<< 11 | 12 | 13 | >>===== CSL =====>> 14 | 33 | <<===== CSL =====<< 34 | 35 | 36 | >>===== INPUT =====>> 37 | [ 38 | { 39 | "id": "ITEM-1", 40 | "issued": { 41 | "date-parts": [ 42 | [ 43 | -250 44 | ] 45 | ] 46 | }, 47 | "title": "Ignore me", 48 | "type": "book" 49 | } 50 | ] 51 | <<===== INPUT =====<< 52 | 53 | 54 | >>===== VERSION =====>> 55 | 1.0 56 | <<===== VERSION =====<< 57 | -------------------------------------------------------------------------------- /tests/local/date_LocalizedDateDelimiters.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | >>===== RESULT =====>> 7 | September 15, 2025 8 | <<===== RESULT =====<< 9 | 10 | 11 | >>===== CSL =====>> 12 | 34 | <<===== CSL =====<< 35 | 36 | 37 | >>===== INPUT =====>> 38 | [ 39 | { 40 | "id": "ITEM-1", 41 | "issued": { 42 | "date-parts": [ 43 | [ 44 | 2025, 45 | 9, 46 | 15 47 | ] 48 | ] 49 | }, 50 | "title": "Ignore me", 51 | "type": "book" 52 | } 53 | ] 54 | <<===== INPUT =====<< 55 | 56 | 57 | >>===== VERSION =====>> 58 | 1.0 59 | <<===== VERSION =====<< 60 | -------------------------------------------------------------------------------- /tests/local/page_NumberPageSort.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | 7 | >>===== RESULT =====>> 8 | 3; 5; 33; 46; 450, 60–30 & 8; 22–45 9 | <<===== RESULT =====<< 10 | 11 | 12 | >>===== CSL =====>> 13 | 31 | <<===== CSL =====<< 32 | 33 | 34 | >>===== INPUT =====>> 35 | [ 36 | { 37 | "id": "ITEM-1", 38 | "page": "3", 39 | "title": "A", 40 | "type": "book" 41 | }, 42 | { 43 | "id": "ITEM-2", 44 | "page": "5", 45 | "title": "B", 46 | "type": "book" 47 | }, 48 | { 49 | "id": "ITEM-3", 50 | "page": "22-45", 51 | "title": "C", 52 | "type": "book" 53 | }, 54 | { 55 | "id": "ITEM-4", 56 | "page": "33", 57 | "title": "D", 58 | "type": "book" 59 | }, 60 | { 61 | "id": "ITEM-5", 62 | "page": "46", 63 | "title": "E", 64 | "type": "book" 65 | }, 66 | { 67 | "id": "ITEM-6", 68 | "page": "450, 60-30 & 8", 69 | "title": "F", 70 | "type": "book" 71 | } 72 | ] 73 | <<===== INPUT =====<< 74 | 75 | 76 | >>===== VERSION =====>> 77 | 1.0 78 | <<===== VERSION =====<< 79 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hayagriva" 3 | version = "0.9.1" 4 | authors = ["Martin Haug "] 5 | edition = "2024" 6 | license = "MIT OR Apache-2.0" 7 | description = "Work with references: Literature database management, storage, and citation formatting" 8 | repository = "https://github.com/typst/hayagriva" 9 | readme = "README.md" 10 | categories = ["template-engine", "value-formatting", "command-line-utilities"] 11 | keywords = ["bibliography", "citation", "reference", "bibtex", "literature"] 12 | 13 | [features] 14 | default = ["biblatex", "archive"] 15 | cli = ["clap", "strum"] 16 | archive = ["ciborium"] 17 | csl-json = ["citationberg/json"] 18 | 19 | [dependencies] 20 | citationberg = "0.6.1" 21 | indexmap = { version = "2.0.2", features = ["serde"] } 22 | roman-numerals-rs = "3.1.0" 23 | paste = "1.0.14" 24 | serde = { version = "1", features = ["derive"] } 25 | serde_yaml = "0.9.25" 26 | thiserror = "2.0.16" 27 | unic-langid = { version = "0.9.6", features = ["serde"] } 28 | unicode-segmentation = "1.6.0" 29 | unscanny = "0.1.0" 30 | url = { version = "2.4", features = ["serde"] } 31 | biblatex = { version = "0.11.0", features = [ 32 | "unic-langid", 33 | ], optional = true } 34 | ciborium = { version = "0.2.1", optional = true } 35 | clap = { version = "4", optional = true, features = ["cargo"] } 36 | strum = { version = "0.26", features = ["derive"], optional = true } 37 | 38 | [dev-dependencies] 39 | heck = "0.5" 40 | serde_json = "1" 41 | regex = "1" 42 | 43 | [[bin]] 44 | name = "hayagriva" 45 | required-features = ["cli"] 46 | 47 | [[test]] 48 | name = "citeproc" 49 | path = "tests/citeproc.rs" 50 | required-features = ["csl-json"] 51 | -------------------------------------------------------------------------------- /tests/local/page_NumberPageMacroSort.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | 7 | >>===== RESULT =====>> 8 | 22–45; 3; 33; 450, 60–30 & 8; 46; 5 9 | <<===== RESULT =====<< 10 | 11 | 12 | >>===== CSL =====>> 13 | 34 | <<===== CSL =====<< 35 | 36 | 37 | >>===== INPUT =====>> 38 | [ 39 | { 40 | "id": "ITEM-1", 41 | "page": "3", 42 | "title": "A", 43 | "type": "book" 44 | }, 45 | { 46 | "id": "ITEM-2", 47 | "page": "5", 48 | "title": "B", 49 | "type": "book" 50 | }, 51 | { 52 | "id": "ITEM-3", 53 | "page": "22-45", 54 | "title": "C", 55 | "type": "book" 56 | }, 57 | { 58 | "id": "ITEM-4", 59 | "page": "33", 60 | "title": "D", 61 | "type": "book" 62 | }, 63 | { 64 | "id": "ITEM-5", 65 | "page": "46", 66 | "title": "E", 67 | "type": "book" 68 | }, 69 | { 70 | "id": "ITEM-6", 71 | "page": "450, 60-30 & 8", 72 | "title": "F", 73 | "type": "book" 74 | } 75 | ] 76 | <<===== INPUT =====<< 77 | 78 | 79 | >>===== VERSION =====>> 80 | 1.0 81 | <<===== VERSION =====<< 82 | -------------------------------------------------------------------------------- /tests/local/position_IbidWithLocatorIsIbid.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | >>===== RESULT =====>> 6 | A 33 7 | ibid. 8 | ibid. 9 | <<===== RESULT =====<< 10 | 11 | >>===== CITATION-ITEMS =====>> 12 | [ 13 | [ 14 | { 15 | "id": "ITEM-1", 16 | "locator": "33" 17 | } 18 | ], 19 | [ 20 | { 21 | "id": "ITEM-1", 22 | "locator": "33" 23 | } 24 | ], 25 | [ 26 | { 27 | "id": "ITEM-1", 28 | "locator": "34" 29 | } 30 | ] 31 | ] 32 | <<===== CITATION-ITEMS =====<< 33 | 34 | >>===== CSL =====>> 35 | 66 | <<===== CSL =====<< 67 | 68 | >>===== INPUT =====>> 69 | [ 70 | { 71 | "id": "ITEM-1", 72 | "title": "A", 73 | "type": "book" 74 | } 75 | ] 76 | <<===== INPUT =====<< 77 | 78 | >>===== VERSION =====>> 79 | 1.0.2 80 | <<===== VERSION =====<< -------------------------------------------------------------------------------- /tests/local/position_IbidIsNotIbidWithLocator.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | >>===== RESULT =====>> 6 | A 33 7 | ibid. 8 | ibid. 34 9 | <<===== RESULT =====<< 10 | 11 | >>===== CITATION-ITEMS =====>> 12 | [ 13 | [ 14 | { 15 | "id": "ITEM-1", 16 | "locator": "33" 17 | } 18 | ], 19 | [ 20 | { 21 | "id": "ITEM-1", 22 | "locator": "33" 23 | } 24 | ], 25 | [ 26 | { 27 | "id": "ITEM-1", 28 | "locator": "34" 29 | } 30 | ] 31 | ] 32 | <<===== CITATION-ITEMS =====<< 33 | 34 | >>===== CSL =====>> 35 | 66 | <<===== CSL =====<< 67 | 68 | >>===== INPUT =====>> 69 | [ 70 | { 71 | "id": "ITEM-1", 72 | "title": "A", 73 | "type": "book" 74 | } 75 | ] 76 | <<===== INPUT =====<< 77 | 78 | >>===== VERSION =====>> 79 | 1.0.2 80 | <<===== VERSION =====<< -------------------------------------------------------------------------------- /src/lang/name.rs: -------------------------------------------------------------------------------- 1 | pub const NAME_PARTICLES: [&str; 115] = [ 2 | "'t", 3 | "a", 4 | "ab", 5 | "af", 6 | "al", 7 | "am", 8 | "and", 9 | "ap", 10 | "auf", 11 | "auf der", 12 | "bar", 13 | "ben", 14 | "bin", 15 | "d'", 16 | "da", 17 | "dai", 18 | "dal", 19 | "dall'", 20 | "dalla", 21 | "dalle", 22 | "de", 23 | "de", 24 | "de", 25 | "de la", 26 | "de las", 27 | "de los", 28 | "de'", 29 | "degli", 30 | "dei", 31 | "del", 32 | "dela", 33 | "dell'", 34 | "della", 35 | "delle", 36 | "dello", 37 | "den", 38 | "den", 39 | "der", 40 | "der", 41 | "des", 42 | "di", 43 | "do", 44 | "dos", 45 | "du", 46 | "e", 47 | "el", 48 | "en", 49 | "et", 50 | "fan", 51 | "filho", 52 | "fitz", 53 | "grosse", 54 | "große", 55 | "i", 56 | "ibn", 57 | "in", 58 | "l'", 59 | "la", 60 | "las", 61 | "le", 62 | "li", 63 | "lo", 64 | "los", 65 | "m'", 66 | "mac", 67 | "mc", 68 | "n'", 69 | "no", 70 | "ní", 71 | "o", 72 | "och", 73 | "of", 74 | "op de", 75 | "saint", 76 | "sainte", 77 | "san", 78 | "sant", 79 | "santa", 80 | "santo", 81 | "st", 82 | "ste", 83 | "t'", 84 | "te", 85 | "ten", 86 | "ten", 87 | "ter", 88 | "ter", 89 | "thoe", 90 | "tot", 91 | "und", 92 | "und der", 93 | "van", 94 | "van", 95 | "van de", 96 | "van den", 97 | "van der", 98 | "van het", 99 | "vande", 100 | "vanden", 101 | "vander", 102 | "ver", 103 | "vom", 104 | "von", 105 | "von den", 106 | "von der", 107 | "von und zu", 108 | "y", 109 | "z", 110 | "ze", 111 | "zu", 112 | "zum", 113 | "zur", 114 | "à", 115 | "é", 116 | "ó", 117 | ]; 118 | -------------------------------------------------------------------------------- /tests/local/collapse_CitationNumberRangesSort.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | Simplified from collapse_CitationNumberRangesInsert.txt 6 | 7 | 8 | >>===== RESULT =====>> 9 | [1]–[4] 10 | [1]–[4] 11 | <<===== RESULT =====<< 12 | 13 | >>===== CITATION-ITEMS =====>> 14 | [ 15 | [ 16 | { 17 | "id": "ITEM-1" 18 | }, 19 | { 20 | "id": "ITEM-2" 21 | }, 22 | { 23 | "id": "ITEM-3" 24 | }, 25 | { 26 | "id": "ITEM-4" 27 | } 28 | ], 29 | [ 30 | { 31 | "id": "ITEM-2" 32 | }, 33 | { 34 | "id": "ITEM-1" 35 | }, 36 | { 37 | "id": "ITEM-4" 38 | }, 39 | { 40 | "id": "ITEM-3" 41 | } 42 | ] 43 | ] 44 | <<===== CITATION-ITEMS =====<< 45 | 46 | 47 | >>===== CSL =====>> 48 | 67 | <<===== CSL =====<< 68 | 69 | 70 | >>===== INPUT =====>> 71 | [ 72 | { 73 | "id": "ITEM-1", 74 | "title": "Paper 1", 75 | "type": "book" 76 | }, 77 | { 78 | "id": "ITEM-2", 79 | "title": "Paper 2", 80 | "type": "book" 81 | }, 82 | { 83 | "id": "ITEM-3", 84 | "title": "Paper 3", 85 | "type": "book" 86 | }, 87 | { 88 | "id": "ITEM-4", 89 | "title": "Paper 4", 90 | "type": "book" 91 | } 92 | ] 93 | <<===== INPUT =====<< 94 | 95 | 96 | >>===== VERSION =====>> 97 | 1.0 98 | <<===== VERSION =====<< 99 | -------------------------------------------------------------------------------- /tests/local/choose_UseAncestorsDelimiter.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | >>===== DESCRIPTION =====>> 7 | Delimiters from the nearest delimiters from the nearest ancestor delimiting element (e.g., ) 8 | are applied within the output of 9 | (i.e., the output of the matching , , or ). 10 | https://docs.citationstyles.org/en/v1.0.2/specification.html#choose 11 | 12 | But they are not applied within the output of a delimiting element or a element. 13 | https://docs.citationstyles.org/en/v1.0.2/specification.html#delimiter 14 | https://docs.citationstyles.org/en/v1.0.2/specification.html#macro 15 | 16 | https://github.com/typst/hayagriva/issues/180 17 | <<===== DESCRIPTION =====<< 18 | 19 | 20 | >>===== RESULT =====>> 21 | Title_Edition_Publisher_Place_TitleEditionPublisherPlace_PublisherPublisherPublisher 22 | <<===== RESULT =====<< 23 | 24 | 25 | >>===== INPUT =====>> 26 | [ 27 | { 28 | "edition": "Edition", 29 | "id": "random", 30 | "publisher": "Publisher", 31 | "publisher-place": "Place", 32 | "title": "Title", 33 | "type": "book" 34 | } 35 | ] 36 | <<===== INPUT =====<< 37 | 38 | 39 | >>===== CSL =====>> 40 | 41 | 77 | <<===== CSL =====<< 78 | 79 | 80 | >>===== VERSION =====>> 81 | 1.0.2 82 | <<===== VERSION =====<< 83 | -------------------------------------------------------------------------------- /tests/local/name_PartInheritsFormatting.txt: -------------------------------------------------------------------------------- 1 | 2 | >>===== MODE =====>> 3 | bibliography 4 | <<===== MODE =====<< 5 | 6 | 7 | 8 | >>===== RESULT =====>> 9 |
10 |
11 | Doe Co. 12 | Doe Co. 13 | Doe Co. 14 | Doe Co. 15 | Doe Co. 16 | Doe Co. 17 |
18 |
19 | <<===== RESULT =====<< 20 | 21 | 22 | >>===== CSL =====>> 23 | 24 | 62 | <<===== CSL =====<< 63 | 64 | 65 | >>===== INPUT =====>> 66 | [ 67 | { 68 | "author": [ 69 | { 70 | "family": "Doe Co.", 71 | "isInstitution": true 72 | } 73 | ], 74 | "id": "ITEM-1", 75 | "issued": { 76 | "date-parts": [ 77 | [ 78 | "1965", 79 | "6", 80 | "1" 81 | ] 82 | ] 83 | }, 84 | "title": "His Collectively Anonymous Life", 85 | "type": "book" 86 | } 87 | ] 88 | <<===== INPUT =====<< 89 | 90 | 91 | >>===== VERSION =====>> 92 | 1.0 93 | <<===== VERSION =====<< 94 | -------------------------------------------------------------------------------- /tests/local/date_NegativeDateSort.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | Sort position of empty dates differs in citations and bibliographies. 7 | In citations, empty dates have a value equivalent to zero. In 8 | bibliographies, they are always placed at the end of the sort 9 | (whether ascending or descending). 10 | 11 | >>===== RESULT =====>> 12 | 101 BC-7-13, 45 BC-3-15, 54 AD-10-13, 68 AD-6-11 13 | <<===== RESULT =====<< 14 | 15 | 16 | >>===== CSL =====>> 17 | 39 | <<===== CSL =====<< 40 | 41 | 42 | >>===== INPUT =====>> 43 | [ 44 | { 45 | "id": "ITEM-1", 46 | "issued": { 47 | "date-parts": [ 48 | [ 49 | -100, 50 | 7, 51 | 13 52 | ] 53 | ] 54 | }, 55 | "title": "BookA", 56 | "type": "book" 57 | }, 58 | { 59 | "id": "ITEM-2", 60 | "issued": { 61 | "date-parts": [ 62 | [ 63 | 54, 64 | 10, 65 | 13 66 | ] 67 | ] 68 | }, 69 | "title": "BookB", 70 | "type": "book" 71 | }, 72 | { 73 | "id": "ITEM-3", 74 | "issued": { 75 | "date-parts": [ 76 | [ 77 | -44, 78 | 3, 79 | 15 80 | ] 81 | ] 82 | }, 83 | "title": "BookC", 84 | "type": "book" 85 | }, 86 | { 87 | "id": "ITEM-4", 88 | "issued": { 89 | "date-parts": [ 90 | [ 91 | 68, 92 | 6, 93 | 11 94 | ] 95 | ] 96 | }, 97 | "title": "BookD", 98 | "type": "book" 99 | } 100 | ] 101 | <<===== INPUT =====<< 102 | 103 | 104 | >>===== VERSION =====>> 105 | 1.0 106 | <<===== VERSION =====<< -------------------------------------------------------------------------------- /tests/local/date_NegativeDateSortViaMacro.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | Sort position of empty dates differs in citations and bibliographies. 7 | In citations, empty dates have a value equivalent to zero. In 8 | bibliographies, they are always placed at the end of the sort 9 | (whether ascending or descending). 10 | 11 | >>===== RESULT =====>> 12 | 101 BC-7-13, 45 BC-3-15, 54 AD-10-13, 68 AD-6-11 13 | <<===== RESULT =====<< 14 | 15 | 16 | >>===== CSL =====>> 17 | 42 | <<===== CSL =====<< 43 | 44 | 45 | >>===== INPUT =====>> 46 | [ 47 | { 48 | "id": "ITEM-1", 49 | "issued": { 50 | "date-parts": [ 51 | [ 52 | -100, 53 | 7, 54 | 13 55 | ] 56 | ] 57 | }, 58 | "title": "BookA", 59 | "type": "book" 60 | }, 61 | { 62 | "id": "ITEM-2", 63 | "issued": { 64 | "date-parts": [ 65 | [ 66 | 54, 67 | 10, 68 | 13 69 | ] 70 | ] 71 | }, 72 | "title": "BookB", 73 | "type": "book" 74 | }, 75 | { 76 | "id": "ITEM-3", 77 | "issued": { 78 | "date-parts": [ 79 | [ 80 | -44, 81 | 3, 82 | 15 83 | ] 84 | ] 85 | }, 86 | "title": "BookC", 87 | "type": "book" 88 | }, 89 | { 90 | "id": "ITEM-4", 91 | "issued": { 92 | "date-parts": [ 93 | [ 94 | 68, 95 | 6, 96 | 11 97 | ] 98 | ] 99 | }, 100 | "title": "BookD", 101 | "type": "book" 102 | } 103 | ] 104 | <<===== INPUT =====<< 105 | 106 | 107 | >>===== VERSION =====>> 108 | 1.0 109 | <<===== VERSION =====<< -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, io, path::PathBuf, process::Command}; 2 | 3 | pub const CACHE_PATH: &str = "target/haya-cache"; 4 | 5 | #[allow(unused)] 6 | pub fn iter_files<'a>( 7 | path: &'a PathBuf, 8 | extension: &'a str, 9 | ) -> impl Iterator + 'a { 10 | iter_files_with_name(path, extension, |_| true) 11 | } 12 | 13 | pub fn iter_files_with_name<'a, F>( 14 | path: &'a PathBuf, 15 | extension: &'a str, 16 | mut name_assertion: F, 17 | ) -> impl Iterator + 'a 18 | where 19 | F: FnMut(&str) -> bool + 'static, 20 | { 21 | fs::read_dir(path).unwrap().filter_map(move |thing| { 22 | let thing = thing.ok()?; 23 | if !thing.file_type().ok()?.is_file() { 24 | return None; 25 | } 26 | 27 | let path = thing.path(); 28 | let thing_ext = path.extension(); 29 | if thing_ext?.to_str() != Some(extension) { 30 | return None; 31 | } 32 | 33 | if !name_assertion(path.file_stem()?.to_str()?) { 34 | return None; 35 | } 36 | 37 | Some(path) 38 | }) 39 | } 40 | 41 | /// Clone a repo if it does not exist, pull it otherwise. 42 | /// 43 | /// This function requires a `git` installation to be available in PATH. 44 | pub fn ensure_repo( 45 | repo_url: &str, 46 | repo_name: &str, 47 | branch_name: &str, 48 | ) -> Result<(), io::Error> { 49 | let cache_path = PathBuf::from(CACHE_PATH); 50 | fs::create_dir_all(CACHE_PATH)?; 51 | 52 | // Check if styles directory exists. If it does, try to git pull origin 53 | // main. If that fails or the directory does not exist, clone the repo. 54 | let style_path = cache_path.join(repo_name); 55 | let clone = if style_path.exists() { 56 | let status = Command::new("git") 57 | .args(["pull", "origin", branch_name]) 58 | .current_dir(&style_path) 59 | .status() 60 | .expect("Please ensure git is installed"); 61 | 62 | if !status.success() { 63 | fs::remove_dir_all(&style_path)?; 64 | true 65 | } else { 66 | false 67 | } 68 | } else { 69 | true 70 | }; 71 | 72 | if clone { 73 | let status = Command::new("git") 74 | .args([ 75 | "clone", 76 | repo_url, 77 | repo_name, 78 | "--depth", 79 | "1", 80 | // Do not convert LF to CRLF. 81 | // Otherwise, CSL/XML may generate different CBOR on Unix and Windows. 82 | "--config", 83 | "core.autocrlf=input", 84 | ]) 85 | .current_dir(&cache_path) 86 | .status() 87 | .expect("Please ensure git is installed"); 88 | 89 | if !status.success() { 90 | return Err(io::Error::other( 91 | "Failed to clone repo. Is git installed correnctly and is the internet working?", 92 | )); 93 | } 94 | } 95 | 96 | Ok(()) 97 | } 98 | -------------------------------------------------------------------------------- /tests/local/date_NegativeDateSortViaMacroOnYearMonthOnly.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | 6 | Sort position of empty dates differs in citations and bibliographies. 7 | In citations, empty dates have a value equivalent to zero. In 8 | bibliographies, they are always placed at the end of the sort 9 | (whether ascending or descending). 10 | 11 | >>===== RESULT =====>> 12 | BookX (101 BC-7-14), BookY (101 BC-7-13), BookA (68 AD-3-16), BookB (68 AD-3-15) 13 | <<===== RESULT =====<< 14 | 15 | 16 | >>===== CSL =====>> 17 | 52 | <<===== CSL =====<< 53 | 54 | 55 | >>===== INPUT =====>> 56 | [ 57 | { 58 | "id": "ITEM-1", 59 | "issued": { 60 | "date-parts": [ 61 | [ 62 | -100, 63 | 7, 64 | 13 65 | ] 66 | ] 67 | }, 68 | "title": "BookY", 69 | "type": "book" 70 | }, 71 | { 72 | "id": "ITEM-2", 73 | "issued": { 74 | "date-parts": [ 75 | [ 76 | -100, 77 | 7, 78 | 14 79 | ] 80 | ] 81 | }, 82 | "title": "BookX", 83 | "type": "book" 84 | }, 85 | { 86 | "id": "ITEM-3", 87 | "issued": { 88 | "date-parts": [ 89 | [ 90 | 68, 91 | 3, 92 | 15 93 | ] 94 | ] 95 | }, 96 | "title": "BookB", 97 | "type": "book" 98 | }, 99 | { 100 | "id": "ITEM-4", 101 | "issued": { 102 | "date-parts": [ 103 | [ 104 | 68, 105 | 3, 106 | 16 107 | ] 108 | ] 109 | }, 110 | "title": "BookA", 111 | "type": "book" 112 | } 113 | ] 114 | <<===== INPUT =====<< 115 | 116 | 117 | >>===== VERSION =====>> 118 | 1.0 119 | <<===== VERSION =====<< 120 | 121 | -------------------------------------------------------------------------------- /tests/local/collapse_CitationNumberRangesSeparated.txt: -------------------------------------------------------------------------------- 1 | >>===== MODE =====>> 2 | citation 3 | <<===== MODE =====<< 4 | 5 | Simplified from collapse_CitationNumberRangesInsert.txt 6 | 7 | 8 | >>===== RESULT =====>> 9 | [1–8] 10 | [1–3;5] 11 | [4,6–8] 12 | [2,3,6] 13 | <<===== RESULT =====<< 14 | 15 | >>===== CITATION-ITEMS =====>> 16 | [ 17 | [ 18 | { 19 | "id": "ITEM-1" 20 | }, 21 | { 22 | "id": "ITEM-2" 23 | }, 24 | { 25 | "id": "ITEM-3" 26 | }, 27 | { 28 | "id": "ITEM-4" 29 | }, 30 | { 31 | "id": "ITEM-5" 32 | }, 33 | { 34 | "id": "ITEM-6" 35 | }, 36 | { 37 | "id": "ITEM-7" 38 | }, 39 | { 40 | "id": "ITEM-8" 41 | } 42 | ], 43 | [ 44 | { 45 | "id": "ITEM-1" 46 | }, 47 | { 48 | "id": "ITEM-2" 49 | }, 50 | { 51 | "id": "ITEM-3" 52 | }, 53 | { 54 | "id": "ITEM-5" 55 | } 56 | ], 57 | [ 58 | { 59 | "id": "ITEM-4" 60 | }, 61 | { 62 | "id": "ITEM-6" 63 | }, 64 | { 65 | "id": "ITEM-7" 66 | }, 67 | { 68 | "id": "ITEM-8" 69 | } 70 | ], 71 | [ 72 | { 73 | "id": "ITEM-2" 74 | }, 75 | { 76 | "id": "ITEM-3" 77 | }, 78 | { 79 | "id": "ITEM-6" 80 | } 81 | ] 82 | ] 83 | <<===== CITATION-ITEMS =====<< 84 | 85 | 86 | >>===== CSL =====>> 87 | 103 | <<===== CSL =====<< 104 | 105 | 106 | >>===== INPUT =====>> 107 | [ 108 | { 109 | "id": "ITEM-1", 110 | "title": "Paper 1", 111 | "type": "book" 112 | }, 113 | { 114 | "id": "ITEM-2", 115 | "title": "Paper 2", 116 | "type": "book" 117 | }, 118 | { 119 | "id": "ITEM-3", 120 | "title": "Paper 3", 121 | "type": "book" 122 | }, 123 | { 124 | "id": "ITEM-4", 125 | "title": "Paper 4", 126 | "type": "book" 127 | }, 128 | { 129 | "id": "ITEM-5", 130 | "title": "Paper 5", 131 | "type": "book" 132 | }, 133 | { 134 | "id": "ITEM-6", 135 | "title": "Paper 6", 136 | "type": "book" 137 | }, 138 | { 139 | "id": "ITEM-7", 140 | "title": "Paper 7", 141 | "type": "book" 142 | }, 143 | { 144 | "id": "ITEM-8", 145 | "title": "Paper 8", 146 | "type": "book" 147 | } 148 | ] 149 | <<===== INPUT =====<< 150 | 151 | 152 | >>===== VERSION =====>> 153 | 1.0 154 | <<===== VERSION =====<< 155 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | //! Reading and writing YAML bibliographies. 2 | 3 | #[cfg(feature = "biblatex")] 4 | use biblatex::{Bibliography, TypeError}; 5 | 6 | #[cfg(feature = "biblatex")] 7 | use crate::Entry; 8 | use crate::Library; 9 | 10 | /// Parse a bibliography from a YAML string. 11 | /// 12 | /// ``` 13 | /// use hayagriva::io::from_yaml_str; 14 | /// 15 | /// let yaml = r#" 16 | /// crazy-rich: 17 | /// type: Book 18 | /// title: Crazy Rich Asians 19 | /// author: Kwan, Kevin 20 | /// date: 2014 21 | /// publisher: Anchor Books 22 | /// location: New York, NY, US 23 | /// "#; 24 | /// let bib = from_yaml_str(yaml).unwrap(); 25 | /// assert_eq!(bib.nth(0).unwrap().date().unwrap().year, 2014); 26 | /// ``` 27 | pub fn from_yaml_str(s: &str) -> Result { 28 | serde_yaml::from_str(s) 29 | } 30 | 31 | /// Serialize a bibliography to a YAML string. 32 | pub fn to_yaml_str(entries: &Library) -> Result { 33 | serde_yaml::to_string(&entries) 34 | } 35 | 36 | /// Errors that may occur when parsing a BibLaTeX file. 37 | #[cfg(feature = "biblatex")] 38 | #[derive(Clone, Debug)] 39 | pub enum BibLaTeXError { 40 | /// An error occurred when parsing a BibLaTeX file. 41 | Parse(biblatex::ParseError), 42 | /// One of the BibLaTeX fields was malformed for its type. 43 | Type(biblatex::TypeError), 44 | } 45 | 46 | #[cfg(feature = "biblatex")] 47 | impl std::fmt::Display for BibLaTeXError { 48 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 49 | match self { 50 | Self::Parse(err) => write!(f, "biblatex parse error: {err}"), 51 | Self::Type(err) => write!(f, "biblatex type error: {err}"), 52 | } 53 | } 54 | } 55 | 56 | /// Parse a bibliography from a BibLaTeX source string. 57 | #[cfg(feature = "biblatex")] 58 | pub fn from_biblatex_str(biblatex: &str) -> Result> { 59 | let bibliography = 60 | Bibliography::parse(biblatex).map_err(|e| vec![BibLaTeXError::Parse(e)])?; 61 | 62 | from_biblatex(&bibliography) 63 | .map_err(|e| e.into_iter().map(BibLaTeXError::Type).collect()) 64 | } 65 | 66 | /// Parse a bibliography from a BibLaTeX [`Bibliography`]. 67 | #[cfg(feature = "biblatex")] 68 | pub fn from_biblatex(bibliography: &Bibliography) -> Result> { 69 | let res: Vec> = 70 | bibliography.iter().map(TryInto::try_into).collect(); 71 | let errors: Vec = res 72 | .iter() 73 | .filter_map(|item| match item { 74 | Ok(_) => None, 75 | Err(err) => Some(err.clone()), 76 | }) 77 | .collect(); 78 | 79 | if !errors.is_empty() { 80 | Err(errors) 81 | } else { 82 | Ok(res.into_iter().map(|item| item.unwrap()).collect()) 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::*; 89 | use std::fs; 90 | 91 | #[test] 92 | fn roundtrip() { 93 | let contents = fs::read_to_string("tests/data/basic.yml").unwrap(); 94 | let entries = from_yaml_str(&contents).unwrap(); 95 | let yaml = to_yaml_str(&entries).unwrap(); 96 | println!("{}", &yaml); 97 | 98 | let reconstructed = from_yaml_str(&yaml).unwrap(); 99 | assert_eq!(entries.len(), reconstructed.len()); 100 | 101 | for entry in entries { 102 | let match_e = reconstructed.iter().find(|x| x.key == entry.key).unwrap(); 103 | assert_eq!(match_e, &entry); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tests/data/art-history.csl: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/csl/citation_label.rs: -------------------------------------------------------------------------------- 1 | //! Alphanumeric labels for citations. 2 | 3 | use std::fmt::Write; 4 | use unicode_segmentation::UnicodeSegmentation; 5 | 6 | use crate::{ 7 | Entry, 8 | types::{EntryType, Person, PersonRole}, 9 | }; 10 | 11 | /// Citation labels in the form of numbers. 12 | /// 13 | /// For example, the output could be Rass97 or MKG+21. \ 14 | /// Corresponds to LaTeX's `alphabetical` style. 15 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 16 | #[non_exhaustive] 17 | pub struct Alphanumerical { 18 | /// Defines how many letters are allowed to describe an entry. 19 | pub letters: usize, 20 | } 21 | 22 | impl Default for Alphanumerical { 23 | fn default() -> Self { 24 | Self::new() 25 | } 26 | } 27 | 28 | impl Alphanumerical { 29 | /// Create a new instance of this [`CitationStyle`]. 30 | pub fn new() -> Self { 31 | Self { letters: 3 } 32 | } 33 | 34 | fn creators(&self, entry: &Entry) -> String { 35 | let creators = get_creators(entry); 36 | 37 | match creators.len() { 38 | 0 => { 39 | let pseudo_creator = if let Some(org) = entry.organization() { 40 | org.to_string() 41 | } else if let Some(title) = entry.get_full().title() { 42 | title.value.to_string() 43 | } else { 44 | entry.key().chars().filter(|c| c.is_alphabetic()).collect::() 45 | }; 46 | 47 | pseudo_creator.graphemes(true).take(self.letters).collect() 48 | } 49 | 1 => creators[0].name.graphemes(true).take(self.letters).collect(), 50 | 2 | 3 => creators 51 | .iter() 52 | .filter_map(|person| person.name.graphemes(true).next()) 53 | .collect(), 54 | _ => creators[0] 55 | .name 56 | .graphemes(true) 57 | .take(self.letters) 58 | .chain(std::iter::once("+")) 59 | .collect(), 60 | } 61 | } 62 | 63 | fn year(entry: &Entry) -> Option { 64 | let year = entry 65 | .date_any() 66 | .or_else(|| entry.url_any().and_then(|u| u.visit_date.as_ref())) 67 | .map(|date| { 68 | let mut year = i32::abs(date.year % 100); 69 | if date.year <= 0 { 70 | year += 1; 71 | } 72 | year 73 | }); 74 | 75 | year.and_then(|y| { 76 | let mut num = String::with_capacity(2); 77 | write!(&mut num, "{y:02}").ok()?; 78 | Some(num) 79 | }) 80 | } 81 | 82 | pub fn citation(self, entry: &Entry) -> String { 83 | let full_entry = entry.get_full(); 84 | 85 | let creators = self.creators(full_entry); 86 | let mut res = creators.clone(); 87 | let year_opt = Self::year(full_entry); 88 | if let Some(year) = year_opt.as_ref() { 89 | res += year; 90 | } 91 | 92 | res 93 | } 94 | } 95 | 96 | /// Get the creator of an entry. 97 | fn get_creators(entry: &Entry) -> Vec<&Person> { 98 | if let Some(authors) = entry.authors() { 99 | authors.iter().collect() 100 | } else if let Some(eds) = entry.editors() { 101 | eds.iter().collect() 102 | } else if entry.entry_type == EntryType::Video { 103 | let tv_series = select!((Video["issue", "volume"]) > ("p":Video)); 104 | if let Some(mut bindings) = tv_series.apply(entry) { 105 | let mut affs = entry.affiliated_with_role(PersonRole::Director); 106 | affs.extend(entry.affiliated_with_role(PersonRole::Writer)); 107 | if !affs.is_empty() { 108 | affs 109 | } else { 110 | let parent = bindings.remove("p").unwrap(); 111 | parent.affiliated_with_role(PersonRole::ExecutiveProducer) 112 | } 113 | } else { 114 | let dir = entry.affiliated_with_role(PersonRole::Director); 115 | if !dir.is_empty() { 116 | dir 117 | } else { 118 | entry.affiliated_with_role(PersonRole::ExecutiveProducer) 119 | } 120 | } 121 | } else { 122 | let compilers = entry.affiliated_with_role(PersonRole::Compiler); 123 | if !compilers.is_empty() { 124 | return compilers; 125 | } 126 | 127 | entry.affiliated_with_role(PersonRole::Translator) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | //! Helpers for serializing and deserializing. 2 | 3 | use serde::{Deserialize, Deserializer, Serialize, de::Visitor}; 4 | 5 | /// Generic wrapper that allow one or more occurrences of specified type. 6 | /// 7 | /// In YAML it will presented or as a value, or as an array: 8 | /// ```yaml 9 | /// one: just a string 10 | /// many: 11 | /// - 1st string 12 | /// - 2nd string 13 | /// ``` 14 | /// 15 | /// Extracted from [`ksc-rs`](https://github.com/Mingun/ksc-rs/) under the MIT 16 | /// license. 17 | #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] 18 | #[serde(untagged)] 19 | pub enum OneOrMany { 20 | /// Single value 21 | One(Box), 22 | /// Array of values 23 | Vec(Vec), 24 | } 25 | 26 | impl From> for Vec { 27 | fn from(from: OneOrMany) -> Self { 28 | match from { 29 | OneOrMany::One(val) => vec![*val], 30 | OneOrMany::Vec(vec) => vec, 31 | } 32 | } 33 | } 34 | 35 | impl Default for OneOrMany { 36 | fn default() -> Self { 37 | OneOrMany::Vec(Vec::new()) 38 | } 39 | } 40 | 41 | impl IntoIterator for OneOrMany { 42 | type Item = T; 43 | type IntoIter = std::vec::IntoIter; 44 | 45 | fn into_iter(self) -> Self::IntoIter { 46 | match self { 47 | OneOrMany::One(val) => vec![*val].into_iter(), 48 | OneOrMany::Vec(vec) => vec.into_iter(), 49 | } 50 | } 51 | } 52 | 53 | /// Function that uses [`OneOrMany`] to serialize. 54 | pub fn serialize_one_or_many(value: &[T], serializer: S) -> Result 55 | where 56 | S: serde::Serializer, 57 | T: Serialize, 58 | { 59 | if value.len() == 1 { 60 | value[0].serialize(serializer) 61 | } else { 62 | value.serialize(serializer) 63 | } 64 | } 65 | 66 | /// Function that uses [`OneOrMany`] to serialize for options. 67 | pub fn serialize_one_or_many_opt( 68 | value: &Option>, 69 | serializer: S, 70 | ) -> Result 71 | where 72 | S: serde::Serializer, 73 | T: Serialize, 74 | { 75 | if let Some(value) = value { 76 | serialize_one_or_many(value, serializer) 77 | } else { 78 | serializer.serialize_none() 79 | } 80 | } 81 | 82 | /// This is a wrapper for [`OneOrMany`] that assumes that the single 83 | /// representation isn't a sequence. This allows better error messages. 84 | #[derive(Clone, Debug, PartialEq, Eq)] 85 | struct MapOneOrMany(OneOrMany); 86 | 87 | impl From> for OneOrMany { 88 | fn from(from: MapOneOrMany) -> Self { 89 | from.0 90 | } 91 | } 92 | 93 | impl From> for Vec { 94 | fn from(from: MapOneOrMany) -> Self { 95 | from.0.into() 96 | } 97 | } 98 | 99 | impl<'de, T> Deserialize<'de> for MapOneOrMany 100 | where 101 | T: Deserialize<'de>, 102 | { 103 | fn deserialize(deserializer: D) -> Result, D::Error> 104 | where 105 | D: Deserializer<'de>, 106 | { 107 | struct MapOneOrManyVisitor(std::marker::PhantomData); 108 | 109 | impl<'de, T> Visitor<'de> for MapOneOrManyVisitor 110 | where 111 | T: Deserialize<'de>, 112 | { 113 | type Value = MapOneOrMany; 114 | 115 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 116 | formatter.write_str("a map, a string, or a list") 117 | } 118 | 119 | fn visit_str(self, v: &str) -> Result 120 | where 121 | E: serde::de::Error, 122 | { 123 | Ok(MapOneOrMany(OneOrMany::One(Box::new(T::deserialize( 124 | serde::de::value::StrDeserializer::new(v), 125 | )?)))) 126 | } 127 | 128 | fn visit_map(self, map: A) -> Result 129 | where 130 | A: serde::de::MapAccess<'de>, 131 | { 132 | Ok(MapOneOrMany(OneOrMany::One(Box::new(T::deserialize( 133 | serde::de::value::MapAccessDeserializer::new(map), 134 | )?)))) 135 | } 136 | 137 | fn visit_seq(self, seq: A) -> Result 138 | where 139 | A: serde::de::SeqAccess<'de>, 140 | { 141 | Ok(MapOneOrMany(OneOrMany::Vec(Vec::deserialize( 142 | serde::de::value::SeqAccessDeserializer::new(seq), 143 | )?))) 144 | } 145 | 146 | fn visit_u64(self, v: u64) -> Result 147 | where 148 | E: serde::de::Error, 149 | { 150 | Ok(MapOneOrMany(OneOrMany::One(Box::new(T::deserialize( 151 | serde::de::value::U64Deserializer::new(v), 152 | )?)))) 153 | } 154 | 155 | fn visit_i64(self, v: i64) -> Result 156 | where 157 | E: serde::de::Error, 158 | { 159 | Ok(MapOneOrMany(OneOrMany::One(Box::new(T::deserialize( 160 | serde::de::value::I64Deserializer::new(v), 161 | )?)))) 162 | } 163 | } 164 | 165 | deserializer.deserialize_any(MapOneOrManyVisitor(std::marker::PhantomData)) 166 | } 167 | } 168 | 169 | /// Function that uses [`MapOneOrMany`] to deserialize. 170 | pub fn deserialize_one_or_many<'de, T, D>(deserializer: D) -> Result, D::Error> 171 | where 172 | D: Deserializer<'de>, 173 | T: Deserialize<'de>, 174 | { 175 | >::deserialize(deserializer).map(|v| v.into()) 176 | } 177 | 178 | /// Function that uses [`MapOneOrMany`] to deserialize for options. 179 | pub fn deserialize_one_or_many_opt<'de, T, D>( 180 | deserializer: D, 181 | ) -> Result>, D::Error> 182 | where 183 | D: Deserializer<'de>, 184 | T: Deserialize<'de>, 185 | { 186 | >>::deserialize(deserializer).map(|v| v.map(|v| v.into())) 187 | } 188 | -------------------------------------------------------------------------------- /docs/selectors.md: -------------------------------------------------------------------------------- 1 | # Selectors 2 | 3 | As you have seen in the Readme and the [file format documentation](https://github.com/typst/hayagriva/blob/main/docs/file-format.md), a Hayagriva entry not only has a type but also parents that can have their own type. With this architecture, differentiation between articles from blogs, newspapers, and conference proceedings is easy without having a dedicated type for each of them! 4 | 5 | However, this also means that you cannot just match on the top-level type of an entry to make all the distinctions that you or your citation and reference styles may require. 6 | 7 | Enter selectors: They provide a convenient way to query entries by structure and retrieve parents that hold crucial information for your use case. 8 | 9 | Two ways of macro usage are offered: Either as a string (used on the command line) or, if you are depending on Hayagriva as a library, with the `select!` macro. Library users can parse string selectors using `Selector::parse`. The Readme explains the fundamental differences between the two formats. If there are divergences between both forms, we will provide both variants as examples. 10 | 11 | ## Entry type selector 12 | 13 | The most basic selectors are entry types; they will match any entry that has the same top-level type: 14 | 15 | | Variant | Example | 16 | |-------------|------------------------------------------------------| 17 | | **String:** | `thesis` | 18 | | **Macro:** | `Thesis` | 19 | 20 | This works with any of the [entry types](https://github.com/typst/hayagriva/blob/main/docs/file-format.md#entry-type). Be aware that you have to capitalize exactly like in the EntryType struct variants when using the macro. 21 | The string selectors are case-insensitive. 22 | 23 | ## Wildcard 24 | 25 | There is also a wildcard selector that applies to every entry: 26 | 27 | ``` 28 | * 29 | ``` 30 | 31 | This might seem pointless on its own but is quite useful in conjunction with other selector constructs. 32 | 33 | ## Required fields 34 | 35 | Sometimes you want to filter for entries that have certain fields set. This can be accomplished with the fields selector. Attach square brackets to a selector and put the fields you want to be set inside, separated by commas. All specified fields have to contain some value for the selector to match. 36 | 37 | | Variant | Example | 38 | |-------------|------------------------------------------------------| 39 | | **String:** | `artwork[archive, archive-location]` | 40 | | **Macro:** | `Artwork["archive", "archive-location"]` | 41 | 42 | This example finds all artworks with a known archive (including its location). The macro needs the attributes to be strings. 43 | 44 | ## Negation 45 | 46 | The exclamation mark allows you to select everything that does not match the following selector. 47 | 48 | | Variant | Example | 49 | |-------------|------------------------------------------------------| 50 | | **String:** | `!article` | 51 | | **Macro:** | `!Article` | 52 | 53 | ## Disjunction 54 | 55 | The disjunction operator `|` allows you to offer multiple alternative selectors, only one of which has to be matched for the whole construction to match. Think about it as an 'or.' 56 | 57 | | Variant | Example 1 | 58 | |-------------|------------------------------------------------------| 59 | | **String:** | `anthology \| *[volume]` | 60 | | **Macro:** | `Anthology \| (*["volume"])` | 61 | 62 | 63 | 64 | Either an anthology or anything with a volume field. 65 | 66 | | Variant | Example 2 | 67 | |-------------|------------------------------------------------------| 68 | | **String:** | `(video \| audio \| web[runtime])[affiliated]` | 69 | | **Macro:** | `(Video \| Audio \| (Web["runtime"]))["affiliated"]` | 70 | 71 | Matches every video, audio, and web (with runtime field) entry, given that it has the affiliated field set. 72 | 73 | ## Ancestrage 74 | 75 | Require that a parent has to match some selector with the `>` operator. The conditions to its left have to apply for the top-level, the selector on the right side must match any of the parents. The operator can also be chained to examine nested parents. 76 | 77 | | Variant | Example 1 | 78 | |-------------|------------------------------------------------------| 79 | | **String:** | `article > proceedings` | 80 | | **Macro:** | `Article > Proceedings` | 81 | 82 | This selector finds published conference articles. 83 | 84 | | Variant | Example 2 | 85 | |-------------|------------------------------------------------------| 86 | | **String:** | `chapter > (book \| anthology) > (book \| anthology)`| 87 | | **Macro:** | `Chapter > (Book \| Anthology) > (Book \| Anthology)`| 88 | 89 | This selects a chapter in a monograph (a long-form text on a subject published in another book). 90 | 91 | ## Bindings 92 | 93 | _Less interesting for CLI users._ 94 | 95 | Once you know that a matching entry has some parents, you might need to access their fields. You can be sure you got the right parent with bindings. 96 | Create a binding using the `:` operator. To the left of the selector, you provide a name which you can freely choose. The parent (or the top-level entry) that matches the selector to the right will then appear in the resulting map under the key that you have chosen. 97 | 98 | | Variant | Example | 99 | |-------------|------------------------------------------------------| 100 | | **String:** | `article > parent:(blog \| newspaper)` | 101 | | **Macro:** | `Article > ("parent":(Blog \| Newspaper))` | 102 | 103 | The binding name has to be a string for macro use. 104 | This binds the blog or newspaper parent of an article to 'parent' if the selector matches. 105 | It is possible to write selectors that only sometimes bind a variable if it matches. 106 | You could reformulate the right part of the above selector as `(parent:blog | newspaper)`, then it would not bind 'parent' if the selector matches with a newspaper-type parent. 107 | 108 | Note that all bindings within a negation are discarded. 109 | 110 | ## Require multiple parents 111 | 112 | Sometimes, a single parent does not provide the full picture. For example, a dataset (type repository) could be both published on the web and presented in a paper, so it would have a `web` and an `article` parent (with the latter possibly having an `periodical` or `proceedings` parent). To capture such entries with selectors, we need to define multiple conditions for parents, that all must be satisfied. This can be done using the `&`-operator. The operator may only be used to the right of an [ancestrage operator](#ancestrage). 113 | 114 | | Variant | Example | 115 | |-------------|------------------------------------------------------| 116 | | **String:** | `article > (conference & video)` | 117 | | **Macro:** | `Article > (Conference & Video)` | 118 | 119 | This selector matches conference talks published as a video. 120 | -------------------------------------------------------------------------------- /src/csl/sort.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | use citationberg::taxonomy::Variable; 4 | use citationberg::{ 5 | DemoteNonDroppingParticle, InheritableNameOptions, LocaleCode, LongShortForm, Sort, 6 | SortDirection, SortKey, 7 | }; 8 | 9 | use crate::csl::BufWriteFormat; 10 | use crate::csl::rendering::RenderCsl; 11 | 12 | use super::taxonomy::EntryLike; 13 | use super::{CitationItem, InstanceContext, StyleContext}; 14 | 15 | impl StyleContext<'_> { 16 | /// Retrieve the ordering of two entries according to the given sort key. 17 | fn cmp_entries( 18 | &self, 19 | a: &CitationItem, 20 | a_idx: usize, 21 | b: &CitationItem, 22 | b_idx: usize, 23 | key: &SortKey, 24 | term_locale: Option<&LocaleCode>, 25 | ) -> Ordering { 26 | let (ordering, empty_value) = match key { 27 | SortKey::Variable { variable: Variable::Standard(s), .. } => { 28 | let a = InstanceContext::variable_sort_instance(a, a_idx) 29 | .resolve_standard_variable(LongShortForm::default(), *s) 30 | .map(|s| s.to_string().to_lowercase()); 31 | let b = InstanceContext::variable_sort_instance(b, b_idx) 32 | .resolve_standard_variable(LongShortForm::default(), *s) 33 | .map(|s| s.to_string().to_lowercase()); 34 | 35 | (a.cmp(&b), a.is_none() || b.is_none()) 36 | } 37 | SortKey::Variable { variable: Variable::Date(d), .. } => { 38 | let a = a.entry.resolve_date_variable(*d); 39 | let b = b.entry.resolve_date_variable(*d); 40 | 41 | match (a, b) { 42 | (Some(a), Some(b)) => (a.csl_cmp(&b), false), 43 | (Some(_), None) => (Ordering::Greater, true), 44 | (None, Some(_)) => (Ordering::Less, true), 45 | (None, None) => (Ordering::Equal, true), 46 | } 47 | } 48 | SortKey::Variable { variable: Variable::Name(n), .. } => { 49 | let a = a.entry.resolve_name_variable(*n); 50 | let b = b.entry.resolve_name_variable(*n); 51 | 52 | for (a_pers, b_pers) in a.iter().zip(b.iter()) { 53 | let ord = a_pers.csl_cmp( 54 | b_pers, 55 | LongShortForm::Long, 56 | self.csl.settings.demote_non_dropping_particle 57 | != DemoteNonDroppingParticle::Never, 58 | ); 59 | if ord != Ordering::Equal { 60 | return ord; 61 | } 62 | } 63 | 64 | ( 65 | if a.len() < b.len() { 66 | Ordering::Less 67 | } else if a.len() > b.len() { 68 | Ordering::Greater 69 | } else { 70 | Ordering::Equal 71 | }, 72 | false, 73 | ) 74 | } 75 | SortKey::Variable { variable: Variable::Number(n), .. } => { 76 | let a = InstanceContext::variable_sort_instance(a, a_idx) 77 | .resolve_number_variable(*n); 78 | let b = InstanceContext::variable_sort_instance(b, b_idx) 79 | .resolve_number_variable(*n); 80 | 81 | match (a, b) { 82 | (Some(a), Some(b)) => (a.csl_cmp(&b), false), 83 | (Some(_), None) => (Ordering::Greater, true), 84 | (None, Some(_)) => (Ordering::Less, true), 85 | (None, None) => (Ordering::Equal, true), 86 | } 87 | } 88 | SortKey::Variable { variable: Variable::Page(pv), .. } => { 89 | let a = InstanceContext::variable_sort_instance(a, a_idx) 90 | .resolve_page_variable(*pv); 91 | let b = InstanceContext::variable_sort_instance(b, b_idx) 92 | .resolve_page_variable(*pv); 93 | 94 | match (a, b) { 95 | (Some(a), Some(b)) => (a.csl_cmp(&b), false), 96 | (Some(_), None) => (Ordering::Greater, true), 97 | (None, Some(_)) => (Ordering::Less, true), 98 | (None, None) => (Ordering::Equal, true), 99 | } 100 | } 101 | SortKey::MacroName { 102 | name, 103 | names_min, 104 | names_use_first, 105 | names_use_last, 106 | .. 107 | } => { 108 | let render = |entry: &CitationItem, idx: usize| { 109 | let mut ctx = self.macro_sorting_ctx( 110 | entry, 111 | idx, 112 | entry.locale.as_ref(), 113 | term_locale, 114 | false, 115 | ); 116 | ctx.writing.name_options.push(InheritableNameOptions { 117 | et_al_min: *names_min, 118 | et_al_subsequent_min: *names_min, 119 | et_al_use_first: *names_use_first, 120 | et_al_subsequent_use_first: *names_use_first, 121 | et_al_use_last: *names_use_last, 122 | ..Default::default() 123 | }); 124 | 125 | self.get_macro(name).map(|m| { 126 | for child in &m.children { 127 | child.render(&mut ctx) 128 | } 129 | ctx.flush().0.into_iter().fold(String::new(), |mut s, f| { 130 | f.write_buf(&mut s, BufWriteFormat::Plain).unwrap(); 131 | s.to_lowercase() 132 | }) 133 | }) 134 | }; 135 | 136 | let a_rendered = render(a, a_idx); 137 | let b_rendered = render(b, b_idx); 138 | 139 | (a_rendered.cmp(&b_rendered), false) 140 | } 141 | }; 142 | 143 | // Per CSL 1.0.2 spec (https://docs.citationstyles.org/en/v1.0.2/specification.html): 144 | // Entries with empty values are always displayed at the end, even with reversed order. 145 | // So we always reverse the order if either entry is empty. 146 | if empty_value || key.sort_direction() == SortDirection::Descending { 147 | ordering.reverse() 148 | } else { 149 | ordering 150 | } 151 | } 152 | 153 | /// Sorts the given citation items by the style's sort keys. 154 | pub fn sort( 155 | &self, 156 | cites: &mut [CitationItem], 157 | sort: Option<&Sort>, 158 | term_locale: Option<&LocaleCode>, 159 | citation_number: impl Fn(&T) -> usize, 160 | ) { 161 | if let Some(sort) = sort { 162 | cites.sort_by(|a, b| { 163 | let mut ordering = Ordering::Equal; 164 | for key in &sort.keys { 165 | ordering = self.cmp_entries( 166 | a, 167 | citation_number(a.entry), 168 | b, 169 | citation_number(b.entry), 170 | key, 171 | term_locale, 172 | ); 173 | if ordering != Ordering::Equal { 174 | break; 175 | } 176 | } 177 | ordering 178 | }); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/lang/en.rs: -------------------------------------------------------------------------------- 1 | pub const ARTICLES: [&str; 8] = ["A", "AN", "An", "THE", "The", "a", "an", "the"]; 2 | 3 | pub const NEVER_CAPITALIZE: [&str; 50] = [ 4 | "A", 5 | "Above", 6 | "Across", 7 | "Against", 8 | "Among", 9 | "An", 10 | "And", 11 | "Around", 12 | "As", 13 | "At", 14 | "Behind", 15 | "Below", 16 | "Beneath", 17 | "Beside", 18 | "Between", 19 | "But", 20 | "By", 21 | "Down", 22 | "During", 23 | "For", 24 | "From", 25 | "Front", 26 | "In", 27 | "Inside", 28 | "Into", 29 | "M", 30 | "N", 31 | "Near", 32 | "Nor", 33 | "Of", 34 | "On", 35 | "Onto", 36 | "Or", 37 | "Over", 38 | "S", 39 | "Since", 40 | "So", 41 | "T", 42 | "The", 43 | "Till", 44 | "To", 45 | "Toward", 46 | "Under", 47 | "Underneath", 48 | "Until", 49 | "Up", 50 | "Via", 51 | "With", 52 | "Within", 53 | "Yet", 54 | ]; 55 | 56 | pub const ALWAYS_CAPITALIZE: [&str; 426] = [ 57 | "ababa", 58 | "abidjan", 59 | "addis", 60 | "afghanistan", 61 | "africa", 62 | "african", 63 | "ahmedabad", 64 | "aires", 65 | "albania", 66 | "alexandria", 67 | "algeria", 68 | "algiers", 69 | "america", 70 | "american", 71 | "amsterdam", 72 | "andorra", 73 | "angeles", 74 | "angola", 75 | "antigua", 76 | "april", 77 | "arab", 78 | "arabia", 79 | "argentina", 80 | "armenia", 81 | "asia", 82 | "asian", 83 | "athens", 84 | "atlanta", 85 | "august", 86 | "australia", 87 | "australian", 88 | "austria", 89 | "azerbaijan", 90 | "baghdad", 91 | "bahamas", 92 | "bahrain", 93 | "bangalore", 94 | "bangkok", 95 | "bangladesh", 96 | "bangladeshi", 97 | "barbados", 98 | "barbuda", 99 | "barcelona", 100 | "beijing", 101 | "belarus", 102 | "belgium", 103 | "belize", 104 | "belo", 105 | "benin", 106 | "berlin", 107 | "bernardino", 108 | "bhutan", 109 | "birmingham", 110 | "bissau", 111 | "bogotá", 112 | "bolivia", 113 | "bonn", 114 | "bosnia", 115 | "boston", 116 | "botswana", 117 | "brasília", 118 | "brazil", 119 | "brazilian", 120 | "british", 121 | "brunei", 122 | "brussels", 123 | "bucharest", 124 | "budapest", 125 | "buenos", 126 | "bulgaria", 127 | "burkina faso", 128 | "burma", 129 | "burundi", 130 | "cabo verde", 131 | "cairo", 132 | "cambodia", 133 | "cameroon", 134 | "canada", 135 | "casablanca", 136 | "chad", 137 | "chengdu", 138 | "chennai", 139 | "chi", 140 | "chicago", 141 | "chile", 142 | "china", 143 | "chinese", 144 | "chongqing", 145 | "cologne", 146 | "colombia", 147 | "comoros", 148 | "congo", 149 | "congo-brazzaville", 150 | "congolese", 151 | "copenhagen", 152 | "costa rica", 153 | "croatia", 154 | "cuba", 155 | "cyprus", 156 | "czech", 157 | "czechia", 158 | "côte", 159 | "d'ivoire", 160 | "dakar", 161 | "dalian", 162 | "dallas", 163 | "dar", 164 | "december", 165 | "delhi", 166 | "denmark", 167 | "detroit", 168 | "dhaka", 169 | "djibouti", 170 | "dominica", 171 | "dominican", 172 | "dongguan", 173 | "douala", 174 | "dr", 175 | "dublin", 176 | "durban", 177 | "ecuador", 178 | "egypt", 179 | "egyptian", 180 | "el", 181 | "emirates", 182 | "england", 183 | "english", 184 | "equatorial", 185 | "eritrea", 186 | "estonia", 187 | "eswatini", 188 | "ethiopia", 189 | "ethiopian", 190 | "europe", 191 | "european", 192 | "february", 193 | "fiji", 194 | "filipino", 195 | "finland", 196 | "fortaleza", 197 | "foshan", 198 | "france", 199 | "francisco", 200 | "frankfurt", 201 | "french", 202 | "friday", 203 | "fukuoka", 204 | "gabon", 205 | "gambia", 206 | "georgia", 207 | "german", 208 | "germany", 209 | "ghana", 210 | "greece", 211 | "grenada", 212 | "grenadines", 213 | "guadalajara", 214 | "guangzhou", 215 | "guatemala", 216 | "guinea", 217 | "guinea", 218 | "guinea-bissau", 219 | "guyana", 220 | "hague", 221 | "haiti", 222 | "hamburg", 223 | "hangzhou", 224 | "hanoi", 225 | "harbin", 226 | "herzegovina", 227 | "ho", 228 | "holy", 229 | "honduras", 230 | "hong", 231 | "horizonte", 232 | "houston", 233 | "hungary", 234 | "hyderabad", 235 | "i", 236 | "ibadan", 237 | "iceland", 238 | "india", 239 | "indian", 240 | "indonesia", 241 | "indonesian", 242 | "iran", 243 | "iranian", 244 | "iraq", 245 | "ireland", 246 | "irish", 247 | "islands", 248 | "israel", 249 | "istanbul", 250 | "italian", 251 | "italy", 252 | "jakarta", 253 | "jamaica", 254 | "janeiro", 255 | "january", 256 | "japan", 257 | "japanese", 258 | "jinan", 259 | "jinjiang", 260 | "johannesburg", 261 | "jordan", 262 | "july", 263 | "june", 264 | "kano", 265 | "karachi", 266 | "kazakhstan", 267 | "kenya", 268 | "khartoum", 269 | "kiev", 270 | "kingdom", 271 | "kinshasa", 272 | "kiribati", 273 | "kitts", 274 | "kobe", 275 | "kolkata", 276 | "kong", 277 | "korea", 278 | "kuala", 279 | "kuwait", 280 | "kyoto", 281 | "kyrgyzstan", 282 | "lagos", 283 | "lahore", 284 | "lanka", 285 | "laos", 286 | "latvia", 287 | "lebanon", 288 | "leone", 289 | "lesotho", 290 | "liberia", 291 | "libya", 292 | "liechtenstein", 293 | "lima", 294 | "lisbon", 295 | "lithuania", 296 | "london", 297 | "los", 298 | "luanda", 299 | "lucia", 300 | "lumpur", 301 | "luxembourg", 302 | "lyon", 303 | "macedonia", 304 | "madagascar", 305 | "madrid", 306 | "malawi", 307 | "malaysia", 308 | "maldives", 309 | "mali", 310 | "malta", 311 | "manchester", 312 | "manila", 313 | "march", 314 | "marino", 315 | "marseille", 316 | "mauritania", 317 | "mauritius", 318 | "may", 319 | "medellín", 320 | "mexico", 321 | "mexican", 322 | "miami", 323 | "micronesia", 324 | "milan", 325 | "minh", 326 | "mogadishu", 327 | "moldova", 328 | "monaco", 329 | "monday", 330 | "mongolia", 331 | "montenegro", 332 | "monterrey", 333 | "montreal", 334 | "morocco", 335 | "moscow", 336 | "mozambique", 337 | "mr", 338 | "mrs", 339 | "mumbai", 340 | "munich", 341 | "myanmar", 342 | "nagoya", 343 | "nairobi", 344 | "namibia", 345 | "nanjing", 346 | "naples", 347 | "nauru", 348 | "nepal", 349 | "netherlands", 350 | "nevis", 351 | "nicaragua", 352 | "niger", 353 | "nigeria", 354 | "nigerian", 355 | "norway", 356 | "november", 357 | "oakland", 358 | "october", 359 | "oman", 360 | "osaka", 361 | "ouagadougou", 362 | "pakistan", 363 | "pakistani", 364 | "palau", 365 | "palestine", 366 | "panama", 367 | "papua", 368 | "paraguay", 369 | "paris", 370 | "paulo", 371 | "peru", 372 | "petersburg", 373 | "philadelphia", 374 | "philippines", 375 | "phoenix", 376 | "poland", 377 | "porto alegre", 378 | "portugal", 379 | "prague", 380 | "pune", 381 | "qatar", 382 | "qingdao", 383 | "quanzhou", 384 | "recife", 385 | "republic", 386 | "rio", 387 | "riverside", 388 | "riyadh", 389 | "romania", 390 | "rome", 391 | "rotterdam", 392 | "ruhr", 393 | "russia", 394 | "russian", 395 | "rwanda", 396 | "saint", 397 | "salaam", 398 | "salvador", 399 | "samoa", 400 | "san", 401 | "santiago", 402 | "sao", 403 | "saturday", 404 | "saudi", 405 | "seattle", 406 | "senegal", 407 | "seoul", 408 | "september", 409 | "serbia", 410 | "seychelles", 411 | "shanghai", 412 | "shenyang", 413 | "shenzhen", 414 | "shishi", 415 | "sierra", 416 | "singapore", 417 | "slovakia", 418 | "slovenia", 419 | "sofia", 420 | "solomon", 421 | "somalia", 422 | "spain", 423 | "sri", 424 | "states", 425 | "stockholm", 426 | "sudan", 427 | "sunday", 428 | "surat", 429 | "suriname", 430 | "suzhou", 431 | "swaziland", 432 | "sweden", 433 | "switzerland", 434 | "syria", 435 | "são", 436 | "tajikistan", 437 | "tanzania", 438 | "tanzanian", 439 | "tehran", 440 | "thai", 441 | "thailand", 442 | "thursday", 443 | "tianjin", 444 | "timor-leste", 445 | "tobago", 446 | "togo", 447 | "tokyo", 448 | "tome", 449 | "tonga", 450 | "toronto", 451 | "trinidad", 452 | "tuesday", 453 | "tunisia", 454 | "turin", 455 | "turkey", 456 | "turkish", 457 | "turkmenistan", 458 | "tuvalu", 459 | "uganda", 460 | "uk", 461 | "ukraine", 462 | "united", 463 | "uruguay", 464 | "us", 465 | "usa", 466 | "uzbekistan", 467 | "vanuatu", 468 | "venezuela", 469 | "vienna", 470 | "vietnam", 471 | "vietnamese", 472 | "vincent", 473 | "warsaw", 474 | "washington", 475 | "wednesday", 476 | "wuhan", 477 | "xi'an", 478 | "yangon", 479 | "yemen", 480 | "york", 481 | "zambia", 482 | "zimbabwe", 483 | ]; 484 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2021] [Martin Haug, Laurenz Mädje, and Hayagriva Contributors] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/selectors/mod.rs: -------------------------------------------------------------------------------- 1 | //! Filter bibliographies by structural selection. 2 | 3 | /// Construct a [`Selector`]. 4 | /// 5 | /// Selectors can be used to filter bibliographies or to differentiate between 6 | /// [`entries`](Entry) during processing. An [introduction to selectors][intro] 7 | /// is available in the Git repository. This macro accepts expressions very 8 | /// similar to the user-facing macros as parsed by [`Selector::parse`]. 9 | /// 10 | /// There are three main differences: 11 | /// - Binding names and attributes have to be strings and thus wrapped in double 12 | /// quotes 13 | /// - Type names are case sensitive and have to start with a capital letter 14 | /// since they are just variants of [`EntryType`][EType] 15 | /// - Any non-atomic selector (`*` or an [`EntryType`][EType] variant are 16 | /// atomic) in other constructions like ancestrage selectors or bindings must 17 | /// be in parentheses since they have to parse as a single token tree for 18 | /// correct expansion. If you get an error message along the lines of having 19 | /// too much recursion, you likely forgot some parentheses. 20 | /// 21 | /// This example illustrates some of the differences: 22 | /// 23 | /// ```rust 24 | /// use hayagriva::select; 25 | /// use hayagriva::Selector; 26 | /// 27 | /// // finds an article that is parented by a conference proceedings volume 28 | /// assert_eq!(Selector::parse("article > proceedings").unwrap(), select!(Article > Proceedings)); 29 | /// 30 | /// // matches either a video or audio item or an artwork 31 | /// assert_eq!(Selector::parse("video | audio | artwork").unwrap(), select!(Video | Audio | Artwork)); 32 | /// 33 | /// // matches anything that is parented by both a blog and a newspaper 34 | /// assert_eq!(Selector::parse("* > (blog | newspaper)").unwrap(), select!(* > (Blog | Newspaper))); 35 | /// 36 | /// // matches anything with a URL or a parent with a URL and binds the entry with the attribute to the variable `i`. 37 | /// // Note that expressions like i:*[url] do not need parentheses in the parsed selector, but they do in the macro! 38 | /// assert_eq!(Selector::parse("i:*[url] | (* > i:*[url])").unwrap(), select!(("i":(*["url"])) | (* > ("i":(*["url"]))))); 39 | /// ``` 40 | /// 41 | /// [intro]: https://github.com/typst/hayagriva/blob/main/docs/selectors.md 42 | /// [EType]: crate::types::EntryType 43 | #[macro_export] 44 | macro_rules! select { 45 | (($($tts:tt)*)) => { 46 | select!($($tts)*) 47 | }; 48 | 49 | (*) => { 50 | $crate::Selector::Wildcard 51 | }; 52 | 53 | ($entry_type:ident) => { 54 | $crate::Selector::Entry($crate::types::EntryType::$entry_type) 55 | }; 56 | 57 | ($binding:literal:$expr:tt) => { 58 | $crate::Selector::Binding( 59 | $binding.to_string(), 60 | Box::new(select!($expr)), 61 | ) 62 | }; 63 | 64 | ($expr:tt[$($attr:literal),* $(,)?]) => { 65 | $crate::Selector::Attr( 66 | Box::new(select!($expr)), 67 | vec![$($attr.to_string()),*], 68 | ) 69 | }; 70 | 71 | (!$expr:tt) => { 72 | $crate::Selector::Neg(Box::new(select!($expr))) 73 | }; 74 | 75 | ($lhs:tt > $rhs:tt) => { 76 | $crate::Selector::Ancestrage( 77 | Box::new(select!($lhs)), 78 | Box::new(select!($rhs)), 79 | ) 80 | }; 81 | 82 | ($($expr:tt)|+) => { 83 | $crate::Selector::Alt(vec![$(select!($expr)),*]) 84 | }; 85 | 86 | ($($expr:tt)&+) => { 87 | $crate::Selector::Multi(vec![$(select!($expr)),*]) 88 | }; 89 | } 90 | 91 | mod parser; 92 | 93 | use std::collections::HashMap; 94 | 95 | use thiserror::Error; 96 | 97 | use crate::Entry; 98 | use crate::types::EntryType; 99 | 100 | /// A selector used to filter bibliographies and match on entries. 101 | #[derive(Debug, Clone, Eq, PartialEq)] 102 | #[non_exhaustive] 103 | pub enum Selector { 104 | /// A wildcard selector: `*`. 105 | Wildcard, 106 | /// An entry type literal: `report`. 107 | Entry(EntryType), 108 | /// A negation: `!x`. 109 | Neg(Box), 110 | /// A binding: `x:misc`. 111 | Binding(String, Box), 112 | /// An attribute filtering: `abc[att1, attr2]`. 113 | Attr(Box, Vec), 114 | /// An alternate selector: `a | b`. 115 | Alt(Vec), 116 | /// A multi-parent selector: `a & b`. 117 | Multi(Vec), 118 | /// An ancestrage selector: `a > b`. 119 | Ancestrage(Box, Box), 120 | } 121 | 122 | impl Selector { 123 | /// Parse a selector from a string. 124 | pub fn parse(src: &str) -> SelectorResult { 125 | parser::parse(src) 126 | } 127 | 128 | /// Checks if the selector matches the provided [`Entry`]. 129 | pub fn matches(&self, entry: &Entry) -> bool { 130 | self.apply(entry).is_some() 131 | } 132 | 133 | /// Applies the selector and returns the bound element if there was a match. 134 | /// 135 | /// This can panic if there are resolving entries which do not bind the 136 | /// argument. 137 | pub fn bound<'s>(&self, entry: &'s Entry, bound: &str) -> Option<&'s Entry> { 138 | self.apply(entry).and_then(|mut hm| hm.remove(bound)) 139 | } 140 | 141 | /// Applies the selector to an [`Entry`] and returns the bound variables 142 | /// in a hash map if there was a match. 143 | pub fn apply<'s>(&self, entry: &'s Entry) -> Option> { 144 | match self { 145 | Self::Wildcard => Some(HashMap::new()), 146 | 147 | Self::Entry(entry_type) => { 148 | if &entry.entry_type == entry_type { 149 | Some(HashMap::new()) 150 | } else { 151 | None 152 | } 153 | } 154 | 155 | Self::Neg(expr) => { 156 | if expr.apply(entry).is_some() { 157 | None 158 | } else { 159 | Some(HashMap::new()) 160 | } 161 | } 162 | 163 | Self::Binding(binding, expr) => expr.apply(entry).map(|mut bound| { 164 | bound.insert(binding.to_string(), entry); 165 | bound 166 | }), 167 | 168 | Self::Attr(expr, attributes) => expr.apply(entry).and_then(|bound| { 169 | if attributes.iter().all(|arg| entry.has(arg.as_ref())) { 170 | Some(bound) 171 | } else { 172 | None 173 | } 174 | }), 175 | 176 | Self::Alt(exprs) => { 177 | for expr in exprs { 178 | let applied = expr.apply(entry); 179 | if applied.is_some() { 180 | return applied; 181 | } 182 | } 183 | None 184 | } 185 | 186 | Self::Multi(_) => None, 187 | 188 | Self::Ancestrage(lhs, rhs) => lhs.apply(entry).and_then(|mut bound| { 189 | let parents = &entry.parents; 190 | if let Some((other, _)) = rhs.apply_any(parents) { 191 | bound.extend(other); 192 | Some(bound) 193 | } else { 194 | None 195 | } 196 | }), 197 | } 198 | } 199 | 200 | fn apply_any<'s>( 201 | &self, 202 | entries: &'s [Entry], 203 | ) -> Option<(HashMap, Vec<&'s Entry>)> { 204 | match self { 205 | Self::Wildcard => { 206 | if !entries.is_empty() { 207 | Some((HashMap::new(), entries.iter().collect())) 208 | } else { 209 | None 210 | } 211 | } 212 | 213 | Self::Entry(_) => entries 214 | .iter() 215 | .filter_map(|e| self.apply(e).map(|r| (r, vec![e]))) 216 | .next(), 217 | 218 | Self::Neg(expr) => { 219 | if expr.apply_any(entries).is_some() { 220 | None 221 | } else { 222 | Some((HashMap::new(), vec![])) 223 | } 224 | } 225 | 226 | Self::Binding(binding, expr) => { 227 | expr.apply_any(entries).map(|(mut bound, es)| { 228 | if let Some(first) = es.first() { 229 | bound.insert(binding.to_string(), first); 230 | } 231 | (bound, vec![]) 232 | }) 233 | } 234 | 235 | Self::Attr(expr, attributes) => { 236 | expr.apply_any(entries).and_then(|(bound, es)| { 237 | if !es.is_empty() { 238 | if es 239 | .iter() 240 | .any(|e| attributes.iter().all(|arg| e.has(arg.as_ref()))) 241 | { 242 | Some((bound, es)) 243 | } else { 244 | None 245 | } 246 | } else { 247 | Some((bound, es)) 248 | } 249 | }) 250 | } 251 | 252 | Self::Alt(exprs) => { 253 | for expr in exprs { 254 | let applied = expr.apply_any(entries); 255 | if applied.is_some() { 256 | return applied; 257 | } 258 | } 259 | None 260 | } 261 | 262 | Self::Multi(exprs) => { 263 | let mut consumed = vec![]; 264 | let mut res = HashMap::new(); 265 | 266 | for spec in exprs { 267 | let mut item = None; 268 | for (i, e) in entries.iter().enumerate() { 269 | if consumed.contains(&i) { 270 | continue; 271 | } 272 | 273 | item = spec.apply(e).map(|v| (i, v)); 274 | if item.is_some() { 275 | break; 276 | } 277 | } 278 | 279 | if let Some((index, bound)) = item { 280 | res.extend(bound); 281 | consumed.push(index); 282 | } else { 283 | return None; 284 | } 285 | } 286 | 287 | let mut es = vec![]; 288 | for i in consumed.into_iter() { 289 | es.push(entries.get(i).unwrap()); 290 | } 291 | 292 | Some((res, es)) 293 | } 294 | 295 | Self::Ancestrage(_, _) => entries 296 | .iter() 297 | .filter_map(|e| self.apply(e).map(|r| (r, vec![e]))) 298 | .next(), 299 | } 300 | } 301 | } 302 | 303 | /// A specialized result type with a selector error. 304 | type SelectorResult = Result; 305 | 306 | /// The error when parsing a selector expression fails. 307 | #[derive(Debug, Clone, Eq, PartialEq, Error)] 308 | pub enum SelectorError { 309 | /// A value (entry type or subexpression) was expected. 310 | #[error("missing value")] 311 | MissingValue, 312 | /// An attribute list contained something expected. 313 | #[error("malformed attribute")] 314 | MalformedAttribute, 315 | /// A comma was expected in an attribute expression. 316 | #[error("missing comma")] 317 | MissingComma, 318 | /// The parentheses are unbalanced. 319 | #[error("unbalanced parentheses")] 320 | UnbalancedParens, 321 | /// The entry type is not known. 322 | #[error("unknown entry type: `{0}`")] 323 | UnknownEntryType(String), 324 | } 325 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.9.1 2 | 3 | - Export the `TransparentLocator` struct so `LocatorPayload::Transparent` can be 4 | constructed (#397) 5 | - Fixes the `location` conditional in CSL styles for citations with no locator (#399) 6 | - Fix a bug where accessing year suffix resulted in wrong CSL renders (#400) 7 | 8 | # 0.9.0 9 | 10 | - Add `chapter` field corresponding to CSL `chapter-number` and BibLaTeX 11 | `chapter` (#361, #383) 12 | - Support locator/supplement in alphanumeric style, also clean it up (#298, 13 | #307, #353) 14 | - Support BibLaTeX `language` field (#317) 15 | - Support date seasons, which are displayed when the month is missing (#391) 16 | - Improve translation of BibLaTeX fields to `genre` and `serial-number` (#296, 17 | #369) 18 | - **Breaking change:** Allow differentiating custom locators/supplements for 19 | styles that handle repeated locators. Due to this change, 20 | `LocatorPayload::Transparent` now contains a `TransparentLocator` (#299) 21 | - **Breaking change:** The types `CitationRequest`, `CitationItem`, 22 | `SpecificLocator` and `LocatorPayload` no longer derive `Hash` and `Eq`. That 23 | also removes some automatically derived traits. 24 | - **Breaking change:** Updated `biblatex` to version 0.11.0 25 | - Update most archived CSL styles and locales 26 | - **Breaking change:** Styles `chicago-fullnotes` and 27 | `modern-humanities-research-association` were renamed to `chicago-notes` and 28 | `modern-humanities-research-association-notes`, and the old names are now 29 | deprecated, but still available. `ArchivedStyle::ChicagoFullnotes` and 30 | `ArchivedStyle::ModernHumanitiesResearchAssociation` were removed and the 31 | discriminant of `ArchivedStyle::ChicagoNotes` has changed. (#350, #372, 32 | #389) 33 | - Fixes and improvements to BibLaTeX parsing (#388) 34 | - Support `%` comment syntax, as well as top-level `@comment{}` 35 | - Allow omitting `editor` for `@InProceedings` 36 | - Fix regression where page variables were no longer supported in styles' 37 | `` elements (#289) 38 | - Fix handling of `ibid-with-locator` and `ibid` positions in styles (#301) 39 | - Fix sorting and formatting of name parts (#287, #313) 40 | - Correctly use terms for "AD" and "BC" from chosen locale (#364) 41 | - Fix year suffix collapsing (#367) 42 | - Fix delimiters in locale-specific date formatting (#385) 43 | - Fix rendering of date ordinals (#366) 44 | - Fix rendering and sorting of dates with BC years (#334, #368) 45 | - Fix sorting for empty sort values (#390) 46 | 47 | # 0.8.1 48 | 49 | - Use editor names in prose and author-only citations if the author names are 50 | unavailable 51 | - Recognize some non-standard but widely used BibLaTeX `editortype`s like 52 | `producer`, `writer`, `scriptwriter`, and `none` (defined by widespread style 53 | `biblatex-chicago` to mean performers within `music` and `video` entries) 54 | - Allow CSL styles to render affixes around the bibliography 55 | - Correctly process the PubMed ID for BibTeX entries with `eprinttype = 56 | {pubmed}` 57 | - Fix bugs around the handling of CSL delimiting characters (#109, #180) 58 | - Whitespace handling for the strings delimiting initialized names has been 59 | improved 60 | - Fix problem with parsing multibyte characters in page ranges that could 61 | prevent Hayagriva from parsing some BibTeX page ranges (#241) 62 | - Uppercase spelling after apostrophes used as quotation marks is now possible 63 | - Fix a panic with the CLI's `--format` argument 64 | - Updated CSL APA style 65 | - Updated CSL locales for Finnish, Swiss German, Austrian German, German, and 66 | Arabic 67 | 68 | # 0.8.0 69 | 70 | - **Breaking change:** Fixed deserialization of page ranges, 71 | removing `From for PageRanges` 72 | - Added support for disambiguation to alphanumeric citation style 73 | - Raised limit for disambiguation resolving in complex cases 74 | - The year 0 will now render as 1BC for CSL 75 | 76 | # 0.7.0 77 | 78 | - **Breaking change:** `Entry::page_range` now returns 79 | `Option<&MaybeTyped>` instead of `Option<&PageRanges>`. This fixes 80 | a panic that occurred when a page range had an unknown format 81 | - `MaybeTyped` now has an `as_typed` method 82 | 83 | # 0.6.0 84 | 85 | - **Breaking change:** Fix that the page range labels were not pluralized, 86 | `NumericValue::Range` now returns an inclusive range (#142) 87 | - **Breaking change:** The field `publisher` can now accept a dictionary with a 88 | `location`. The top-level `location` key is now primarily for event and item 89 | locations. 90 | - **Breaking change:** The field `annote` has been removed 91 | - Allow multiple page ranges with prefixes and suffixes 92 | - Fixes with sorting bibliography entries 93 | - Fix sorting citations by their number (#115) 94 | - Fix how citation number ranges collapse (#154) 95 | - `BibliographyItem` is now exported (#157) 96 | - Fix when the short form of a title was used (#173) 97 | - Bumped the `biblatex` crate to 0.10.0 to fix a BibLaTeX parsing bug 98 | (https://github.com/typst/biblatex/issues/53) and allow the Unknown and 99 | Director editor types (https://github.com/typst/biblatex/issues/52). 100 | 101 | We also updated our collection of Citation Styles. 102 | 103 | # 0.5.3 104 | 105 | - Fixed a bug with initials (#150) 106 | - Fixed suppression of title when no author was provided (#144) 107 | - Fixed et al handling on subsequent citations by [bumping citationberg](https://github.com/typst/citationberg/releases/tag/v0.3.1) 108 | 109 | # 0.5.2 110 | 111 | - Allow the `abstract`, `annote`, and `genre` fields to Hayagriva files and process them from BibTeX files. 112 | - Fix retrieval of an item's editor (#94) 113 | - Fixed issue with pulling punctuation into quotation marks (#85) 114 | - Allow non-range values in the `pages` field (#103) 115 | - Fix multiple subsequent prose citations to the same item (#122) 116 | - Interpret the `eprint` BibTeX key as `serial-number.arxiv` if the `eprinttype` is set to `arxiv` 117 | - Fixed issue with multiple subsequent citations (#122) 118 | - Improved handling of empty CSL objects 119 | 120 | # 0.5.1 121 | 122 | - Fixed spacing around math blocks 123 | - Fixed title case formatting next to `{verbatim}` blocks and apostrophes 124 | 125 | # 0.5.0 126 | 127 | - **Breaking change:** The API for archived styles has changed. 128 | - **Breaking change:** The name of the GB/T 7714 family of styles have been 129 | corrected to `gb-7714-...` from `gb-7114-...`. 130 | - **Breaking change:** The reexported `TypeErrorKind` and `ParseErrorKind` enums 131 | in `biblatex` have added variants and become non-exhaustive. 132 | - Date parsing will not panic anymore 133 | (https://github.com/typst/typst/issues/2553). 134 | - Anthos entries now properly recognize their parent (#72, 135 | https://github.com/typst/typst/issues/2572). Thanks, @zepinglee! 136 | - Proceedings titles will now be printed correctly (#78). Thanks, @vtta! 137 | - Citation numbers will now collapse if the style requests it. 138 | - Escaping in format and chunked strings now works 139 | (https://github.com/typst/typst/issues/2669). 140 | - The old behavior of the alphanumeric style has been restored. 141 | - Bibliographies now forcibly print the alphanumeric `citation-label` instead of 142 | the `citation-number` if the cite only printed the former (and vice-versa; 143 | https://github.com/typst/typst/issues/2707). 144 | - We dropped the dependency on `rkyv` in favor of code generation in a test. 145 | This should resolve build problems on some platforms. 146 | - The retrieval of the volume variable is now more robust (#82). Thanks, 147 | @mpmdean! 148 | - Fixed delimiter order for contributors (#73). Thanks, @zepinglee! 149 | - Page ranges can now be strings (#83). 150 | - Page ranges will now use the correct delimiter, even if printed with `cs:text` 151 | - Fixed a bug with the suppression of empty groups 152 | (https://github.com/typst/typst/issues/2548). 153 | - Bumped `citationberg` to solve a CSL locale fallback issue that affected 154 | https://github.com/typst/typst/issues/2548 155 | - Bumped the `biblatex` crate to 0.9.0 to fix BibLaTeX parsing bugs (e.g. 156 | https://github.com/typst/biblatex/issues/41, 157 | https://github.com/typst/biblatex/issues/33, 158 | https://github.com/typst/biblatex/issues/40, 159 | https://github.com/typst/typst/issues/2751, #81) 160 | 161 | # 0.4.0 162 | 163 | ## Breaking changes: 164 | 165 | Hayagriva now uses the [Citation Style Language](https://citationstyles.org) to 166 | encode formatting styles. This means that Hayagriva's own formatting styles have 167 | been deprecated. 168 | 169 | ### For users: 170 | - The YAML input format has changed. 171 | - Titles and formattable strings have been merged into one type. All 172 | formattable strings can have a shorthand now. 173 | - Formattable Strings do not have `title-case` and `sentence-case` keys 174 | anymore. `shorthand` has been renamed to `short`. To prevent changes of 175 | the text case of formattable strings, you can use braces. Enclose a part 176 | of a formattable string (or `short`) in `{braces}` to print it as-is. 177 | - The fields `doi`, `isbn`, and `issn` have been moved to `serial-number` 178 | which can now be a dictionary containing these and arbitrary other serial 179 | numbers like a `pmid` (PubMed ID) and `arxiv` (ArXiv Identifier). 180 | - The `tweet` entry type has been renamed to `post`. 181 | - All numeric variables can now also contains strings. Numbers can have 182 | string affixes. 183 | 184 | Refer to the updated 185 | [file format](https://github.com/typst/hayagriva/blob/main/docs/file-format.md) 186 | docs for examples. 187 | 188 | ### For developers: 189 | - To use a CSL style, you can either supply a CSL file or use an archive of 190 | provided styles with the `archive` feature. 191 | - The `from_yaml_str` function will now return the new `Library` struct, with the 192 | entries within. 193 | - The `Database` struct has been replaced by the easier to handle 194 | `BibliographyDriver`. 195 | - We switched from `yaml_rust` to `serde_yaml`. The `Entry` now implements 196 | `serde`'s `Serialize` and `Deserialize` traits. Hence, the `from_yaml` and 197 | `to_yaml` functions have been deleted. 198 | - Brackets are no longer individually overridable. Instead, use the new 199 | `CitePurpose`. 200 | - `Entry::kind` has been renamed to `Entry::entry_type`. 201 | - The citation styles `AuthorTitle` and `Keys` have been removed but can be 202 | realized with CSL. 203 | 204 | This release fixes many bugs and makes Hayagriva a serious contender for 205 | reference management. 206 | 207 | ## Other changes 208 | 209 | - We added the entry types `Performance` and `Original`. 210 | - We added the field `call-number`. 211 | 212 | 213 | # 0.3.2 214 | 215 | Fixes a title case formatting bug introduced in the previous release. 216 | 217 | # 0.3.1 218 | 219 | _Bug Fixes:_ 220 | - Added an option to turn off abbreviation of journals (thanks to @CMDJojo) 221 | - Fixed bugs with title case formatting (thanks to @jmskov) 222 | - Fixed off-by-one error with dates in APA style (thanks to @bluebear94) 223 | - Fixed supplements in the Alphanumeric and AuthorTitle styles (thanks to @lynn) 224 | - Fixed bugs with sentence case formatting 225 | - Fixed `verbatim` option 226 | - Fixed terminal formatting 227 | - Fixed some typos (thanks to @kianmeng and @bluebear94) 228 | 229 | # 0.3.0 230 | 231 | *Breaking:* 232 | - Updated to `biblatex` 0.8.0 233 | 234 | *Bug Fixes:* 235 | - Fixed string indexing for titles, removed panic 236 | - More permissive BibLaTeX parsing 237 | 238 | # 0.2.1 239 | 240 | *Bug Fixes:* 241 | - Fixed APA bibliography ordering 242 | 243 | # 0.2.0 244 | 245 | *Breaking:* 246 | - Replaced `NoHyphenation` formatting with `Link` formatting 247 | - Switched to newest BibLaTeX (which is part of the public API) 248 | 249 | *Bug Fixes:* 250 | - Fixed IEEE bibliography ordering 251 | - Fixed A, B, C, ... suffixes for Author Date citations 252 | - Removed `println` calls 253 | 254 | # 0.1.1 255 | 256 | 🐞 This release fixes the documentation of the CLI in the `README.md` file. 257 | ✨ There are new options for bracketed citations in the CLI. 258 | ✅ No breaking changes. 259 | 260 | # 0.1.0 261 | 262 | 🎉 This is the initial release! 263 | -------------------------------------------------------------------------------- /src/selectors/parser.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use super::{Selector, SelectorError, SelectorResult}; 4 | use crate::types::EntryType; 5 | 6 | /// Parse a selector. 7 | pub fn parse(src: &str) -> SelectorResult { 8 | let mut p = Parser::new(src); 9 | expr(&mut p) 10 | } 11 | 12 | /// Parse an expression, with optional ancestrage relation. 13 | fn expr(p: &mut Parser) -> SelectorResult { 14 | let mut lhs = term(p)?; 15 | while p.eat_if(Token::Chevron) { 16 | lhs = Selector::Ancestrage(Box::new(lhs), Box::new(term(p)?)); 17 | } 18 | Ok(lhs) 19 | } 20 | 21 | /// Parse a term, consisting of alternatives or multi-parents. 22 | fn term(p: &mut Parser) -> SelectorResult { 23 | let mut lhs = binding(p)?; 24 | 25 | loop { 26 | if p.eat_if(Token::Pipe) { 27 | let mut alternatives = vec![lhs]; 28 | loop { 29 | alternatives.push(binding(p)?); 30 | if !p.eat_if(Token::Pipe) { 31 | break; 32 | } 33 | } 34 | lhs = Selector::Alt(alternatives); 35 | } else if p.eat_if(Token::Ampersand) { 36 | let mut parents = vec![lhs]; 37 | loop { 38 | parents.push(binding(p)?); 39 | if !p.eat_if(Token::Ampersand) { 40 | break; 41 | } 42 | } 43 | lhs = Selector::Multi(parents); 44 | } else { 45 | break; 46 | } 47 | } 48 | 49 | Ok(lhs) 50 | } 51 | 52 | /// Parse an expression with an optional binding `a:expr`. 53 | fn binding(p: &mut Parser) -> SelectorResult { 54 | let start = p.index(); 55 | if let Some(id) = ident(p) { 56 | if p.eat_if(Token::Colon) { 57 | return Ok(Selector::Binding(id, Box::new(attributes(p)?))); 58 | } else { 59 | p.jump(start); 60 | } 61 | } 62 | attributes(p) 63 | } 64 | 65 | /// Parse a factor with optional attributes: `factor[attr, ...]`. 66 | fn attributes(p: &mut Parser) -> SelectorResult { 67 | let inner = factor(p)?; 68 | if p.eat_if(Token::LeftBracket) { 69 | let mut attrs: Vec = vec![]; 70 | loop { 71 | match p.eat() { 72 | Some(Token::RightBracket) => break, 73 | Some(Token::Ident(id)) => attrs.push(id.into()), 74 | _ => return Err(SelectorError::MalformedAttribute), 75 | } 76 | 77 | match p.eat() { 78 | Some(Token::RightBracket) => break, 79 | Some(Token::Comma) => continue, 80 | _ => return Err(SelectorError::MissingComma), 81 | } 82 | } 83 | Ok(Selector::Attr(Box::new(inner), attrs)) 84 | } else { 85 | Ok(inner) 86 | } 87 | } 88 | 89 | /// Parse a value with optional negation: `!value`. 90 | fn factor(p: &mut Parser) -> SelectorResult { 91 | if p.eat_if(Token::ExclamationMark) { 92 | Ok(Selector::Neg(Box::new(factor(p)?))) 93 | } else { 94 | value(p) 95 | } 96 | } 97 | 98 | /// Parse a parenthesized or atomic value: `book`, `(expr)`. 99 | fn value(p: &mut Parser) -> SelectorResult { 100 | match p.eat() { 101 | Some(Token::LeftParen) => { 102 | let expr = expr(p)?; 103 | if !p.eat_if(Token::RightParen) { 104 | return Err(SelectorError::UnbalancedParens); 105 | } 106 | Ok(expr) 107 | } 108 | Some(Token::Star) => Ok(Selector::Wildcard), 109 | Some(Token::Ident(id)) => { 110 | let lower = id.to_lowercase(); 111 | if let Ok(kind) = EntryType::from_str(&lower) { 112 | Ok(Selector::Entry(kind)) 113 | } else { 114 | Err(SelectorError::UnknownEntryType(lower)) 115 | } 116 | } 117 | _ => Err(SelectorError::MissingValue), 118 | } 119 | } 120 | 121 | /// Parse an identifier. 122 | fn ident(p: &mut Parser) -> Option { 123 | p.eat_map(|token| match token { 124 | Token::Ident(id) => Some(id.into()), 125 | _ => None, 126 | }) 127 | } 128 | 129 | /// A token-based parser. 130 | #[derive(Debug)] 131 | struct Parser<'s> { 132 | tokens: Tokens<'s>, 133 | } 134 | 135 | impl<'s> Parser<'s> { 136 | /// Create a new parser for the source string. 137 | fn new(src: &'s str) -> Self { 138 | Self { tokens: Tokens::new(src) } 139 | } 140 | 141 | /// Consume the next token. 142 | fn eat(&mut self) -> Option> { 143 | self.tokens.next() 144 | } 145 | 146 | /// Consume the next token if it is the given one. 147 | fn eat_if(&mut self, t: Token) -> bool { 148 | let matches = self.peek() == Some(t); 149 | if matches { 150 | self.eat(); 151 | } 152 | matches 153 | } 154 | 155 | /// Consume the next token if the closure maps it a to `Some`-variant. 156 | fn eat_map(&mut self, f: impl FnOnce(Token<'s>) -> Option) -> Option { 157 | let mapped = f(self.peek()?); 158 | if mapped.is_some() { 159 | self.eat(); 160 | } 161 | mapped 162 | } 163 | 164 | /// Peek at the next token without consuming it. 165 | fn peek(&mut self) -> Option> { 166 | self.tokens.clone().next() 167 | } 168 | 169 | /// The position in the string at which the last token ends and next token 170 | /// will start. 171 | fn index(&self) -> usize { 172 | self.tokens.s.cursor() 173 | } 174 | 175 | /// Jump to a position in the source string. 176 | fn jump(&mut self, index: usize) { 177 | self.tokens.s.jump(index); 178 | } 179 | } 180 | 181 | /// An iterator over the tokens of a string of source code. 182 | #[derive(Debug, Clone)] 183 | struct Tokens<'s> { 184 | s: unscanny::Scanner<'s>, 185 | } 186 | 187 | /// A minimal semantic entity of source code. 188 | #[derive(Debug, Copy, Clone, PartialEq)] 189 | enum Token<'s> { 190 | /// A left bracket starting an attribute list: `[`. 191 | LeftBracket, 192 | /// A right bracket ending an attribute list: `]`. 193 | RightBracket, 194 | /// A left parenthesis in a option list: `(`. 195 | LeftParen, 196 | /// A right parenthesis in a option list: `)`. 197 | RightParen, 198 | 199 | /// A star replacing a type: `*`. 200 | Star, 201 | /// A colon in a binding: `:`. 202 | Colon, 203 | /// A comma in an attribute list: `,`. 204 | Comma, 205 | /// A pipe in a option list: `|`. 206 | Pipe, 207 | /// An ampersand in a option list: `&`. 208 | Ampersand, 209 | /// A chevron in a ancestrage chain: `>`. 210 | Chevron, 211 | /// A exclamation mark in a negation: `!`. 212 | ExclamationMark, 213 | 214 | /// An identifier in a function header: `Periodical`. 215 | Ident(&'s str), 216 | 217 | /// Things that are not valid in the context they appeared in. 218 | Invalid, 219 | } 220 | 221 | impl<'s> Tokens<'s> { 222 | /// Create a new token iterator with the given mode. 223 | fn new(src: &'s str) -> Self { 224 | Self { s: unscanny::Scanner::new(src) } 225 | } 226 | } 227 | 228 | impl<'s> Iterator for Tokens<'s> { 229 | type Item = Token<'s>; 230 | 231 | /// Parse the next token in the source code. 232 | fn next(&mut self) -> Option { 233 | let mut start = self.s.cursor(); 234 | let mut c = self.s.eat()?; 235 | 236 | while c.is_whitespace() { 237 | start = self.s.cursor(); 238 | c = self.s.eat()?; 239 | } 240 | 241 | Some(match c { 242 | // Brackets. 243 | '[' => Token::LeftBracket, 244 | ']' => Token::RightBracket, 245 | '(' => Token::LeftParen, 246 | ')' => Token::RightParen, 247 | 248 | // List separators. 249 | ',' => Token::Comma, 250 | '&' => Token::Ampersand, 251 | '|' => Token::Pipe, 252 | 253 | // Misc. 254 | '*' => Token::Star, 255 | ':' => Token::Colon, 256 | '>' => Token::Chevron, 257 | '!' => Token::ExclamationMark, 258 | 259 | // Identifiers. 260 | c if is_id_start(c) => { 261 | let mut end = self.s.cursor(); 262 | while let Some(next) = self.s.eat() { 263 | if !is_id_continue(next) { 264 | self.s.jump(end); 265 | break; 266 | } 267 | end = self.s.cursor(); 268 | } 269 | Token::Ident(&self.s.string()[start..end]) 270 | } 271 | 272 | _ => Token::Invalid, 273 | }) 274 | } 275 | } 276 | 277 | fn is_id_start(c: char) -> bool { 278 | c.is_ascii_alphabetic() || c == '-' 279 | } 280 | 281 | fn is_id_continue(c: char) -> bool { 282 | c.is_ascii_alphanumeric() || c == '-' 283 | } 284 | 285 | #[cfg(test)] 286 | mod tests { 287 | use std::fmt::Debug; 288 | 289 | use super::super::Selector; 290 | use super::*; 291 | 292 | use Token::{ 293 | Ampersand as A, ExclamationMark, Ident as Id, LeftBracket as LB, LeftParen as L, 294 | Pipe, RightBracket as RB, RightParen as R, *, 295 | }; 296 | 297 | fn check(src: &str, exp: T, found: T) 298 | where 299 | T: Debug + PartialEq, 300 | { 301 | if exp != found { 302 | println!("source: {src:#?}"); 303 | println!("expected: {exp:#?}"); 304 | println!("found: {found:#?}"); 305 | panic!("test failed"); 306 | } 307 | } 308 | 309 | #[test] 310 | fn test_tokenize() { 311 | macro_rules! t { 312 | ($src:expr => $($token:expr),*) => { 313 | let exp = vec![$($token),*]; 314 | let found = Tokens::new($src).collect::>(); 315 | check($src, exp, found); 316 | } 317 | } 318 | 319 | t!("Article > Book" => Id("Article"), Chevron, Id("Book")); 320 | t!("g5:(Blog | Misc)" => Id("g5"), Colon, L, Id("Blog"), Pipe, Id("Misc"), R); 321 | t!("anthology[editor,date]" => Id("anthology"), LB, Id("editor"), Comma, Id("date"), RB); 322 | t!("alpha:!* > (a & b)" => Id("alpha"), Colon, ExclamationMark, Star, Chevron, 323 | LeftParen, Id("a"), A, Id("b"), R); 324 | } 325 | 326 | #[test] 327 | fn test_parse() { 328 | macro_rules! t { 329 | ($src:expr => $exp:expr) => { 330 | check($src, $exp, Selector::parse($src).unwrap()); 331 | }; 332 | } 333 | 334 | t!("*[title]" => select!(*["title"])); 335 | t!("* > i:*[url]" => select!(* > ("i":(*["url"])))); 336 | t!("* > i:!Blog" => select!(* > ("i":(!Blog)))); 337 | t!("a:Misc" => select!("a":Misc)); 338 | t!("bread:!!blog" => select!("bread":(!(!Blog)))); 339 | t!("anthology[title, author]" => select!(Anthology["title", "author"])); 340 | t!("article > proceedings" => select!(Article > Proceedings)); 341 | t!("artwork | audio > exhibition" => select!((Artwork | Audio) > Exhibition)); 342 | 343 | t!("article > (book & (repository | anthology > blog) & web[url, title])" 344 | => select!(Article > (Book & ((Repository | Anthology) > Blog) & (Web["url", "title"])))); 345 | 346 | t!("a:(book | anthology) > b:((repository > web) | blog)" 347 | => select!(("a":(Book | Anthology)) > ("b":((Repository > Web) | Blog)))); 348 | 349 | t!("a:!audio > ((blog[author] & web) | (video > web))" 350 | => select!(("a":(!Audio)) > (((Blog["author"]) & Web) | (Video > Web)))); 351 | } 352 | 353 | #[test] 354 | fn test_parse_errors() { 355 | assert_eq!(parse("()"), Err(SelectorError::MissingValue)); 356 | assert_eq!(parse("book[*]"), Err(SelectorError::MalformedAttribute)); 357 | assert_eq!(parse("book[date url]"), Err(SelectorError::MissingComma)); 358 | assert_eq!(parse("(book | blog"), Err(SelectorError::UnbalancedParens)); 359 | assert_eq!(parse("a"), Err(SelectorError::UnknownEntryType("a".into()))); 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hayagriva 2 | 3 | [![Build status](https://github.com/typst/hayagriva/workflows/Continuous%20integration/badge.svg)](https://github.com/typst/hayagriva/actions) 4 | [![Current crates.io release](https://img.shields.io/crates/v/hayagriva)](https://crates.io/crates/hayagriva) 5 | [![Documentation](https://img.shields.io/badge/docs.rs-hayagriva-66c2a5?labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K)](https://docs.rs/hayagriva/) 6 | 7 | Rusty bibliography management. 8 | 9 | Hayagriva is a tool that can help you or your apps deal with literature and 10 | other media. Its features include: 11 | 12 | - Data structures for literature collections 13 | - Reading and writing said collections from YAML files 14 | - Formatting literature into reference list entries and in-text citations as 15 | defined by popular style guides 16 | - Interoperability with BibTeX 17 | - Querying your literature items by type and available metadata 18 | 19 | Hayagriva can be used both as a library and as a Command Line Interface (CLI). 20 | Skip to the [section "Usage"](#usage) for more information about usage in your 21 | application or to the [section "Installation"](#installation) to learn about how 22 | to install and use Hayagriva on your terminal. 23 | 24 | ## Supported styles 25 | 26 | Hayagriva supports all styles provided in the 27 | [official Citation Style Language repository](https://github.com/citation-style-language/styles), 28 | currently over 2,600. 29 | 30 | ## Usage 31 | 32 | ```rust 33 | use hayagriva::io::from_yaml_str; 34 | 35 | let yaml = r#" 36 | crazy-rich: 37 | type: Book 38 | title: Crazy Rich Asians 39 | author: Kwan, Kevin 40 | date: 2014 41 | publisher: Anchor Books 42 | location: New York, NY, US 43 | "#; 44 | 45 | // Parse a bibliography 46 | let bib = from_yaml_str(yaml).unwrap(); 47 | assert_eq!(bib.get("crazy-rich").unwrap().date().unwrap().year, 2014); 48 | 49 | // Format the reference 50 | use std::fs; 51 | use hayagriva::{ 52 | BibliographyDriver, BibliographyRequest, BufWriteFormat, 53 | CitationItem, CitationRequest, 54 | }; 55 | use hayagriva::citationberg::{LocaleFile, IndependentStyle}; 56 | 57 | let en_locale = fs::read_to_string("tests/data/locales-en-US.xml").unwrap(); 58 | let locales = [LocaleFile::from_xml(&en_locale).unwrap().into()]; 59 | 60 | let style = fs::read_to_string("tests/data/art-history.csl").unwrap(); 61 | let style = IndependentStyle::from_xml(&style).unwrap(); 62 | 63 | let mut driver = BibliographyDriver::new(); 64 | 65 | for entry in bib.iter() { 66 | let items = vec![CitationItem::with_entry(entry)]; 67 | driver.citation(CitationRequest::from_items(items, &style, &locales)); 68 | } 69 | 70 | let result = driver.finish(BibliographyRequest { 71 | style: &style, 72 | locale: None, 73 | locale_files: &locales, 74 | }); 75 | 76 | for cite in result.citations { 77 | println!("{}", cite.citation.to_string()) 78 | } 79 | ``` 80 | 81 | To format entries, you need to wrap them in a `CitationRequest`. Each of these 82 | can reference multiple entries in their respective `CitationItem`s. 83 | Use these with a `BibliographyDriver` to obtain formatted citations and bibliographies. 84 | 85 | You can either supply your own CSL files or choose from about 100 bundled 86 | citation styles using the `archive` feature. 87 | 88 | If the default features are enabled, Hayagriva supports BibTeX and BibLaTeX 89 | bibliographies. You can use `io::from_biblatex_str` to parse such 90 | bibliographies. 91 | 92 | Should you need more manual control, the library's native `Entry` struct also 93 | offers an implementation of the `From<&biblatex::Entry>`-Trait. You will need to 94 | depend on the [biblatex](https://docs.rs/biblatex/latest/biblatex/) crate to 95 | obtain its `Entry`. Therefore, you could also use your BibLaTeX content like 96 | this: 97 | 98 | ```rust 99 | use hayagriva::Entry; 100 | let converted: Entry = your_biblatex_entry.into(); 101 | ``` 102 | 103 | If you do not need BibLaTeX compatibility, you can use Hayagriva without the 104 | default features by writing this in your `Cargo.toml`: 105 | 106 | ```toml 107 | [dependencies] 108 | hayagriva = { version = "0.9", default-features = false } 109 | ``` 110 | 111 | ### Selectors 112 | 113 | Hayagriva uses a custom selector language that enables you to filter 114 | bibliographies by type of media. For more information about selectors, refer to 115 | the [selectors.md 116 | file](https://github.com/typst/hayagriva/blob/main/docs/selectors.md). While you 117 | can parse user-defined selectors using the function `Selector::parse`, you may 118 | instead want to use the selector macro to avoid the run time cost of parsing a 119 | selector when working with constant selectors. 120 | 121 | ```rust 122 | use hayagriva::select; 123 | use hayagriva::io::from_yaml_str; 124 | 125 | let yaml = r#" 126 | quantized-vortex: 127 | type: Article 128 | author: Gross, E. P. 129 | title: Structure of a Quantized Vortex in Boson Systems 130 | date: 1961-05 131 | page-range: 454-477 132 | doi: 10.1007/BF02731494 133 | parent: 134 | issue: 3 135 | volume: 20 136 | title: Il Nuovo Cimento 137 | "#; 138 | 139 | let entries = from_yaml_str(yaml).unwrap(); 140 | let journal = select!((Article["date"]) > ("journal":Periodical)); 141 | assert!(journal.matches(entries.nth(0).unwrap())); 142 | ``` 143 | 144 | There are two ways to check if a selector matches an entry. 145 | You should use [`Selector::matches`] if you just want to know if an item 146 | matches a selector and [`Selector::apply`] to continue to work with the data from 147 | parents of a matching entry. Keep in mind that the latter function will 148 | return `Some` even if no sub-entry was bound / if the hash map is empty. 149 | 150 | ## Installation 151 | 152 | Run this in your terminal: 153 | 154 | ```bash 155 | cargo install hayagriva --features cli 156 | ``` 157 | 158 | Cargo will install the Hayagriva Command Line Interface for you. Now, you just 159 | need a Hayagriva YAML literature file or a Bib(La)TeX file to get started. The 160 | Hayagriva YAML file is intuitive to write and can represent a wealth of media 161 | types, [learn how to write one in its dedicated 162 | documentation.](https://github.com/typst/hayagriva/blob/main/docs/file-format.md) 163 | 164 | Suppose you have this file saved as `literature.yaml` in your current working 165 | directory: 166 | 167 | ```yaml 168 | dependence: 169 | type: Article 170 | title: The program dependence graph and its use in optimization 171 | author: ["Ferrante, Jeanne", "Ottenstein, Karl J.", "Warren, Joe D."] 172 | date: 1987-07 173 | serial-number: 174 | doi: "10.1145/24039.24041" 175 | parent: 176 | type: Periodical 177 | title: ACM Transactions on Programming Languages and Systems 178 | volume: 9 179 | issue: 3 180 | 181 | feminism: 182 | type: Article 183 | title: She swoons to conquer 184 | author: Ungard-Sargon, Batya 185 | editor: Weintraub, Pam 186 | date: 2015-09-25 187 | url: https://aeon.co/essays/can-you-enjoy-romance-fiction-and-be-a-feminist 188 | parent: 189 | type: Blog 190 | title: Aeon 191 | ``` 192 | 193 | You can then issue the following command to get reference list entries for both 194 | of these articles. 195 | 196 | ```bash 197 | hayagriva literature.yaml reference 198 | ``` 199 | 200 | Hayagriva defaults to the Author-Date style of the Chicago Manual of Style (17th 201 | edition). If you prefer to use another style, you can, for example, do the 202 | following to use the style of the American Psychological Association instead: 203 | 204 | ```bash 205 | hayagriva literature.yaml reference --style apa 206 | ``` 207 | 208 | Available values for the `--style` argument can be viewed by calling 209 | `hayagriva help reference`. 210 | 211 | If you now need an in-text citation to the second article in the above file, you 212 | can call: 213 | 214 | ```bash 215 | hayagriva literature.yaml cite --key feminism 216 | ``` 217 | 218 | The `--key` takes a comma-separated list of keys (or a single one). The 219 | sub-command will then only work on the specified keys. Just like the `reference` 220 | sub-command, the `cite` command also allows the `--style` argument. Its possible 221 | values can be viewed with `hayagriva help cite`. It will default to the _Author 222 | Date_ style. 223 | 224 | Instead of the `--key` argument, you can also use `--select` to provide a custom 225 | [Hayagriva selector.](https://github.com/typst/hayagriva/blob/main/docs/selectors.md) 226 | For example, you could run the following to only reference entries that have a 227 | URL or DOI at the top level: 228 | 229 | ```bash 230 | hayagriva literature.yaml --select "*[url] | *[doi]" reference 231 | ``` 232 | 233 | This expression would match both entries in our example and therefore the 234 | command would return the same result as the first reference command. 235 | 236 | Hayagriva also allows you to explore which values were bound to which 237 | sub-entries if the selector matches. This is especially useful if you intend to 238 | consume Hayagriva as a dependency in your application and need to debug an 239 | expression. Consider this selector which always binds the sub-entry with the 240 | volume field to `a`, regardless of if it occurred at the top level or in the 241 | first parent: `a:*[volume] | * > a:[volume]`. You can then use the command below 242 | to show which sub-entry the selector bound as `a` for each match: 243 | 244 | ```bash 245 | hayagriva literature.yaml --select "a:*[volume] | * > a:[volume]" --show-bound 246 | ``` 247 | 248 | The `--show-bound` flag shows all keys matching the selector or key filter and 249 | details which sub-entries of each entry were bound by the selector. If, instead, 250 | you only want to obtain a list of matching keys, use the `--keys` flag. 251 | 252 | If you are working with BibTeX, you can use your `.bib` file with Hayagriva 253 | just like you would use a `.yaml`/`.yml` file. If you want to convert your 254 | `.bib` file to a `.yaml` file, you can simply pass the `.bib` file to the CLI 255 | without any additional arguments. It will then show the YAML-formatted 256 | bibliography with key and selector filters applied on standard output. If you 257 | therefore want to convert your `.bib` file and save the result somewhere, you 258 | can just use `>`: 259 | 260 | ```bash 261 | hayagriva literature.bib > converted.yaml 262 | ``` 263 | 264 | ## Contributing 265 | 266 | We are looking forward to receiving your bugs and feature requests in the Issues 267 | tab. We would also be very happy to accept PRs for bug fixes, minor 268 | refactorings, features that were requested in the issues and greenlit by us, as 269 | well as the planned features listed below: 270 | 271 | - Implementing the YAML-to-BibLaTeX conversion 272 | - Documentation improvements 273 | - CSL bugfixes 274 | - CSL-M Support 275 | 276 | We wish to thank each and every prospective contributor for the effort you (plan 277 | to) invest in this project and for adopting it! 278 | 279 | ## License 280 | 281 | Hayagriva is licensed under a MIT / Apache 2.0 dual license. 282 | 283 | Users and consumers of the library may choose which of those licenses they want 284 | to apply whereas contributors have to accept that their code is in compliance 285 | and distributed under the terms of both of these licenses. 286 | 287 | Hayagriva includes CSL styles that are licensed as CC-BY-SA 3.0 Deed if the 288 | `archive` feature is enabled. The file `styles.cbor.rkyv` is a collection of 289 | these works and falls under this license. Retrieve attribution information by 290 | deserializing it using the `styles` function and reading the `StyleInfo` 291 | structs. 292 | -------------------------------------------------------------------------------- /src/types/page.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp::Ordering, fmt::Display, num::TryFromIntError, str::FromStr}; 2 | 3 | use crate::{MaybeTyped, Numeric, NumericError}; 4 | 5 | use super::{custom_deserialize, serialize_display}; 6 | use serde::{Deserialize, Serialize}; 7 | use thiserror::Error; 8 | 9 | impl MaybeTyped { 10 | /// Order the values according to CSL rules. 11 | pub(crate) fn csl_cmp(&self, other: &Self) -> std::cmp::Ordering { 12 | match (self, other) { 13 | (MaybeTyped::Typed(a), MaybeTyped::Typed(b)) => a.csl_cmp(b), 14 | _ => self.to_string().cmp(&other.to_string()), 15 | } 16 | } 17 | } 18 | 19 | /// Ranges of page numbers, e.g., `1-4, 5 & 6`. 20 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 21 | pub struct PageRanges { 22 | /// The given ranges. 23 | pub ranges: Vec, 24 | } 25 | 26 | custom_deserialize!( 27 | PageRanges where "pages, page ranges, ampersands, and commas" 28 | fn visit_i32(self, v: i32) -> Result { 29 | Ok(PageRanges::from(v)) 30 | } 31 | fn visit_u32(self, v: u32) -> Result { 32 | PageRanges::try_from(v).map_err(|_| E::custom("value too large")) 33 | } 34 | fn visit_i64(self, v: i64) -> Result { 35 | PageRanges::try_from(v).map_err(|_| E::custom("value out of bounds")) 36 | } 37 | fn visit_u64(self, v: u64) -> Result { 38 | PageRanges::try_from(v).map_err(|_| E::custom("value too large")) 39 | } 40 | ); 41 | 42 | impl PageRanges { 43 | /// Create a new `PageRanges` struct. 44 | pub fn new(ranges: Vec) -> Self { 45 | Self { ranges } 46 | } 47 | 48 | /// Get the first page of the first range. 49 | pub fn first(&self) -> Option<&Numeric> { 50 | self.ranges.iter().find_map(PageRangesPart::start) 51 | } 52 | 53 | /// Order the values according to CSL rules. 54 | pub(crate) fn csl_cmp(&self, other: &Self) -> std::cmp::Ordering { 55 | #[derive(PartialEq, Eq)] 56 | struct OrderablePageRangesPart<'a>(&'a PageRangesPart); 57 | 58 | impl Ord for OrderablePageRangesPart<'_> { 59 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 60 | self.0.csl_cmp(other.0) 61 | } 62 | } 63 | 64 | impl PartialOrd for OrderablePageRangesPart<'_> { 65 | fn partial_cmp(&self, other: &Self) -> Option { 66 | Some(self.cmp(other)) 67 | } 68 | } 69 | 70 | self.ranges 71 | .iter() 72 | .map(OrderablePageRangesPart) 73 | .cmp(other.ranges.iter().map(OrderablePageRangesPart)) 74 | } 75 | 76 | /// Whether to pluralize the `pages` term, when used with this page range. 77 | pub fn is_plural(&self) -> bool { 78 | let mut count = 0; 79 | for range in &self.ranges { 80 | match range { 81 | PageRangesPart::SinglePage(_) => count += 1, 82 | PageRangesPart::Range(s, e) | PageRangesPart::EscapedRange(s, e) => { 83 | if s != e { 84 | return true; 85 | } 86 | count += 1 87 | } 88 | _ => {} 89 | } 90 | } 91 | count > 1 92 | } 93 | } 94 | 95 | impl From for PageRanges { 96 | fn from(value: i32) -> Self { 97 | Self { ranges: vec![value.into()] } 98 | } 99 | } 100 | 101 | impl TryFrom for PageRanges { 102 | type Error = TryFromIntError; 103 | 104 | fn try_from(value: u32) -> Result { 105 | Ok(Self { ranges: vec![value.try_into()?] }) 106 | } 107 | } 108 | 109 | impl TryFrom for PageRanges { 110 | type Error = TryFromIntError; 111 | 112 | fn try_from(value: i64) -> Result { 113 | Ok(Self { ranges: vec![value.try_into()?] }) 114 | } 115 | } 116 | 117 | impl TryFrom for PageRanges { 118 | type Error = TryFromIntError; 119 | 120 | fn try_from(value: u64) -> Result { 121 | Ok(Self { ranges: vec![value.try_into()?] }) 122 | } 123 | } 124 | 125 | impl Display for PageRanges { 126 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 127 | self.ranges.iter().try_for_each(|r| r.fmt(f)) 128 | } 129 | } 130 | 131 | impl FromStr for PageRanges { 132 | type Err = PageRangesPartErr; 133 | 134 | fn from_str(s: &str) -> Result { 135 | // Split input into different ranges separated by `&` or `,` 136 | Ok(Self { 137 | ranges: group_by(s, |c, d| !(c == ',' || c == '&' || d == ',' || d == '&')) 138 | .map(PageRangesPart::from_str) 139 | .collect::>()?, 140 | }) 141 | } 142 | } 143 | 144 | /// Parts of the page ranges. 145 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 146 | pub enum PageRangesPart { 147 | /// An and, i.e, `&`. 148 | Ampersand, 149 | /// A comma, i.e., `,`. 150 | Comma, 151 | /// An escaped range with start and end, e.g., `1\-4`. 152 | EscapedRange(Numeric, Numeric), 153 | /// A single page, e.g., `5`. 154 | SinglePage(Numeric), 155 | /// A full range, e.g., `1n8--1n14`. 156 | Range(Numeric, Numeric), 157 | } 158 | 159 | custom_deserialize!( 160 | PageRangesPart where "a page, a page range, or a separator" 161 | fn visit_i32(self, v: i32) -> Result { 162 | Ok(PageRangesPart::from(v)) 163 | } 164 | fn visit_u32(self, v: u32) -> Result { 165 | PageRangesPart::try_from(v).map_err(|_| E::custom("value too large")) 166 | } 167 | fn visit_i64(self, v: i64) -> Result { 168 | PageRangesPart::try_from(v).map_err(|_| E::custom("value out of bounds")) 169 | } 170 | fn visit_u64(self, v: u64) -> Result { 171 | PageRangesPart::try_from(v).map_err(|_| E::custom("value too large")) 172 | } 173 | ); 174 | 175 | impl PageRangesPart { 176 | /// The start of a range, if any. 177 | pub fn start(&self) -> Option<&Numeric> { 178 | match self { 179 | Self::EscapedRange(s, _) => Some(s), 180 | Self::SinglePage(s) => Some(s), 181 | Self::Range(s, _) => Some(s), 182 | _ => None, 183 | } 184 | } 185 | 186 | /// The end of a range, if any. 187 | pub fn end(&self) -> Option<&Numeric> { 188 | match self { 189 | Self::EscapedRange(_, e) => Some(e), 190 | Self::Range(_, e) => Some(e), 191 | Self::SinglePage(_) => None, 192 | _ => None, 193 | } 194 | } 195 | 196 | /// Order the values according to CSL rules. 197 | pub(crate) fn csl_cmp(&self, other: &Self) -> std::cmp::Ordering { 198 | match (self, other) { 199 | (Self::Ampersand, Self::Ampersand) => Ordering::Equal, 200 | (Self::Ampersand, _) => Ordering::Less, 201 | (_, Self::Ampersand) => Ordering::Greater, 202 | (Self::Comma, Self::Comma) => Ordering::Equal, 203 | (Self::Comma, _) => Ordering::Less, 204 | (_, Self::Comma) => Ordering::Greater, 205 | (Self::SinglePage(n1), Self::SinglePage(n2)) => n1.csl_cmp(n2), 206 | (Self::SinglePage(_), _) => Ordering::Less, 207 | (_, Self::SinglePage(_)) => Ordering::Greater, 208 | (Self::EscapedRange(s1, e1), Self::EscapedRange(s2, e2)) => { 209 | let ord = s1.csl_cmp(s2); 210 | if ord != Ordering::Equal { 211 | return ord; 212 | } 213 | e1.csl_cmp(e2) 214 | } 215 | (Self::EscapedRange(_, _), _) => Ordering::Less, 216 | (_, Self::EscapedRange(_, _)) => Ordering::Greater, 217 | (Self::Range(s1, e1), Self::Range(s2, e2)) => { 218 | let ord = s1.csl_cmp(s2); 219 | if ord != Ordering::Equal { 220 | return ord; 221 | } 222 | e1.csl_cmp(e2) 223 | } 224 | } 225 | } 226 | } 227 | 228 | impl From for PageRangesPart { 229 | fn from(value: i32) -> Self { 230 | Self::SinglePage(value.into()) 231 | } 232 | } 233 | 234 | impl TryFrom for PageRangesPart { 235 | type Error = TryFromIntError; 236 | 237 | fn try_from(value: u32) -> Result { 238 | let value: i32 = value.try_into()?; 239 | Ok(Self::SinglePage(value.into())) 240 | } 241 | } 242 | 243 | impl TryFrom for PageRangesPart { 244 | type Error = TryFromIntError; 245 | 246 | fn try_from(value: i64) -> Result { 247 | let value: i32 = value.try_into()?; 248 | Ok(Self::SinglePage(value.into())) 249 | } 250 | } 251 | 252 | impl TryFrom for PageRangesPart { 253 | type Error = TryFromIntError; 254 | 255 | fn try_from(value: u64) -> Result { 256 | let value: i32 = value.try_into()?; 257 | Ok(Self::SinglePage(value.into())) 258 | } 259 | } 260 | 261 | impl Display for PageRangesPart { 262 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 263 | let s = match self { 264 | PageRangesPart::Ampersand => "&", 265 | PageRangesPart::Comma => ", ", 266 | PageRangesPart::EscapedRange(s, e) => return write!(f, "{s}-{e}"), 267 | PageRangesPart::SinglePage(s) => return write!(f, "{s}"), 268 | PageRangesPart::Range(s, e) => return write!(f, "{s}-{e}"), 269 | }; 270 | Display::fmt(s, f) 271 | } 272 | } 273 | 274 | /// Parsing error for page ranges. 275 | #[derive(Debug, Clone, Copy, Error)] 276 | pub enum PageRangesPartErr { 277 | /// The string is malformed. 278 | #[error("page range string malformed")] 279 | Malformed, 280 | /// The string is empty. 281 | #[error("page range is empty")] 282 | Empty, 283 | /// An error from parsing a numeric value. 284 | #[error("page range contained invalid numeric value")] 285 | NumericErr(#[from] NumericError), 286 | } 287 | 288 | impl FromStr for PageRangesPart { 289 | type Err = PageRangesPartErr; 290 | 291 | fn from_str(s: &str) -> Result { 292 | let s = s.trim(); 293 | if s.is_empty() { 294 | return Err(PageRangesPartErr::Empty); 295 | } 296 | let p = if s == "&" { 297 | Self::Ampersand 298 | } else if s == "," { 299 | Self::Comma 300 | } else if s.contains("\\-") { 301 | // If `-` chars are escaped, write `-`. 302 | let mut parts = s.split("\\-").map(str::trim); 303 | 304 | let start = parts.next().ok_or(PageRangesPartErr::Empty)?; 305 | let end = parts.next().ok_or(PageRangesPartErr::Empty)?; 306 | 307 | let r = Self::EscapedRange(parse_number(start)?, parse_number(end)?); 308 | if parts.next().is_some() { 309 | return Err(PageRangesPartErr::Malformed); 310 | } 311 | r 312 | } else { 313 | // Otherwise, split into the two halves of the dash. 314 | let mut parts = s.split(['-', '–']).map(str::trim); 315 | let r = match (parts.next(), parts.next()) { 316 | (None, None) => unreachable!(), 317 | (Some(start), None) => Self::SinglePage(parse_number(start)?), 318 | (Some(start), Some(end)) => { 319 | Self::Range(parse_number(start)?, parse_number(end)?) 320 | } 321 | _ => unreachable!(), 322 | }; 323 | if parts.next().is_some() { 324 | return Err(PageRangesPartErr::Malformed); 325 | } 326 | r 327 | }; 328 | Ok(p) 329 | } 330 | } 331 | 332 | serialize_display!(PageRanges); 333 | 334 | fn parse_number(s: &str) -> Result { 335 | Numeric::from_str(s) 336 | } 337 | 338 | /// Split `s` into maximal chunks such that two successive chars satisfy `pred`. 339 | /// 340 | /// Returns an iterator over these chunks. 341 | pub(crate) fn group_by(s: &str, pred: F) -> GroupBy<'_, F> 342 | where 343 | F: FnMut(char, char) -> bool, 344 | { 345 | GroupBy::new(s, pred) 346 | } 347 | 348 | /// An iterator over string slice in (non-overlapping) chunks separated by a predicate. 349 | /// 350 | /// Adapted from the nightly std. 351 | pub(crate) struct GroupBy<'a, P> { 352 | string: &'a str, 353 | predicate: P, 354 | } 355 | 356 | impl<'a, P> GroupBy<'a, P> { 357 | pub(crate) fn new(string: &'a str, predicate: P) -> Self { 358 | GroupBy { string, predicate } 359 | } 360 | } 361 | 362 | impl<'a, P> Iterator for GroupBy<'a, P> 363 | where 364 | P: FnMut(char, char) -> bool, 365 | { 366 | type Item = &'a str; 367 | 368 | #[inline] 369 | fn next(&mut self) -> Option { 370 | if let Some(first_char) = self.string.chars().next() { 371 | let mut len = first_char.len_utf8(); 372 | for (c, d) in self.string.chars().zip(self.string.chars().skip(1)) { 373 | if (self.predicate)(c, d) { 374 | len += d.len_utf8(); 375 | } else { 376 | break; 377 | } 378 | } 379 | let (head, tail) = self.string.split_at(len); 380 | self.string = tail; 381 | Some(head) 382 | } else { 383 | None 384 | } 385 | } 386 | 387 | #[inline] 388 | fn size_hint(&self) -> (usize, Option) { 389 | self.string.chars().size_hint() 390 | } 391 | } 392 | 393 | #[cfg(test)] 394 | mod test { 395 | #[test] 396 | fn group_by() { 397 | fn group(s: &str) -> Vec<&'_ str> { 398 | super::group_by(s, |c, d| !(c == ',' || c == '&' || d == ',' || d == '&')) 399 | .collect() 400 | } 401 | assert_eq!(["a"], group("a").as_slice()); 402 | assert_eq!(["a", ","], group("a,").as_slice()); 403 | assert_eq!([",", "a"], group(",a").as_slice()); 404 | assert_eq!([",", "a", ","], group(",a,").as_slice()); 405 | assert_eq!(["a", ",", "b"], group("a,b").as_slice()); 406 | assert_eq!(["a-"], group("a-").as_slice()); 407 | // characters that are longer than 1 byte 408 | assert_eq!(["a–"], group("a–").as_slice()); 409 | assert_eq!(["–a"], group("–a").as_slice()); 410 | assert_eq!(["–a", ","], group("–a,").as_slice()); 411 | assert_eq!(["a–", ",", "–b"], group("a–,–b").as_slice()); 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /tests/data/basic.yml: -------------------------------------------------------------------------------- 1 | zygos: 2 | type: Article 3 | author: 4 | - Prekas, George 5 | - Kogias, Marios 6 | - Bugnion, Edouard 7 | title: 8 | value: "ZygOS: Achieving Low Tail Latency for Microsecond-Scale Networked Tasks" 9 | verbatim: true 10 | date: 2017 11 | page-range: 325-341 12 | serial-number: 13 | doi: 10.1145/3132747.3132780 14 | parent: 15 | - type: Proceedings 16 | title: Proceedings of the 26th Symposium on Operating Systems Principles 17 | publisher: Association for Computing Machinery 18 | location: New York, NY, USA 19 | serial-number: { isbn: "978-1450350853" } 20 | 21 | wwdc-network: 22 | type: Article 23 | author: ["Mehta, Jiten", "Kinnear, Eric"] 24 | title: Boost Performance and Security with Modern Networking 25 | date: 2020-06-26 26 | parent: 27 | - type: Conference 28 | title: 29 | value: "{World Wide Developer Conference 2020}" 30 | short: WWDC 2020 31 | organization: Apple Inc. 32 | location: Mountain View, CA 33 | - type: Video 34 | runtime: "00:13:42" 35 | url: { value: https://developer.apple.com/videos/play/wwdc2020/10111/, date: 2020-09-17 } 36 | 37 | omarova-libra: 38 | type: Article 39 | author: ["Omarova, Saule", "Steele, Graham"] 40 | title: 41 | value: There’s a Lot We Still Don’t Know About Libra 42 | sentence-case: There’s a lot we still don’t know about Libra 43 | date: 2019-11-04 44 | url: https://www.nytimes.com/2019/11/04/opinion/facebook-libra-cryptocurrency.html 45 | parent: 46 | type: Newspaper 47 | title: 48 | value: The New York Times 49 | verbatim: true 50 | location: "New York, NY, USA" 51 | 52 | donne: 53 | type: Book 54 | title: The "Anniversaries" and the "Epicedes and Obsequies" 55 | author: Donne, John 56 | editor: ["Stringer, Gary A.", "Pebworth, Ted-Larry"] 57 | location: Bloomington 58 | volume: 6 59 | publisher: Indiana University Press 60 | date: 1995 61 | parent: 62 | type: Book 63 | title: The Variorum Edition of the Poetry of John Donne 64 | editor: Stringer, Gary A. 65 | 66 | swedish: 67 | type: Article 68 | editor: Brown, George C. 69 | title: 70 | value: "A Swedish Traveller in Early Wisconsin: The Observations of Fredrika Bremer" 71 | short: Swedish Traveller 72 | volume: 1-2 73 | parent: 74 | type: Periodical 75 | title: Wisconsin Magazine of History 76 | volume: 61-62 77 | date: 1978 78 | edition: 2 79 | 80 | harry: 81 | type: Chapter 82 | page-range: 135-139 83 | serial-number: 3 84 | parent: 85 | title: Harry Potter and the Order of the Phoenix 86 | author: Rowling, J. K. 87 | volume: 5 88 | volume-total: 7 89 | serial-number: { isbn: 978-0747551003 } 90 | page-total: 768 91 | date: ~2003-06-21 92 | 93 | science-e-issue: 94 | type: Web 95 | title: Tokenization of + and - with scientific notation 96 | url: https://github.com/typst/typstc/issues/3 97 | serial-number: 3 98 | author: 99 | - name: "Mädje" 100 | given-name: "Laurenz" 101 | alias: "laurmaedje" 102 | date: 2020-07-18 103 | parent: 104 | type: Repository 105 | title: Typst 106 | url: https://github.com/typst/typst 107 | 108 | terminator-2: 109 | type: Video 110 | title: "Terminator 2: Judgment Day" 111 | publisher: Carolco Pictures; Pacific Western Productions; Lightstorm Entertainment; Le Studio Canal+ S.A. 112 | affiliated: 113 | - role: director 114 | names: Cameron, James 115 | - role: cast-member 116 | names: ["Schwarzenegger, Arnold", "Hamilton, Linda", "Patrick, Robert"] 117 | - role: composer 118 | names: Fiedel, Brad 119 | date: 1991-07-01 120 | runtime: "137:00" 121 | time-range: "17:05-17:48" 122 | 123 | interior: 124 | type: Video 125 | title: "Conspiracy Theories and Interior Design" 126 | affiliated: 127 | - role: director 128 | names: Davidson, Adam 129 | - role: writer 130 | names: McKenna, Chris 131 | date: 2010-11-18 132 | runtime: "22:00" 133 | volume: 2 134 | issue: 9 135 | parent: 136 | title: Community 137 | affiliated: 138 | - role: executive-producer 139 | names: Harmon, Dan 140 | publisher: Universal Television; Sony Pictures Television; Krasnoff Foster Productions; Harmonious Claptrap; Russo Brothers Film 141 | volume-total: 6 142 | 143 | wire: 144 | type: Video 145 | title: The wire 146 | date: 2002 147 | publisher: Blown Deadline Productions 148 | volume-total: 5 149 | # From Wikipedia, the free encyclopedia: https://en.wikipedia.org/wiki/The_Wire 150 | abstract: | 151 | The Wire is an American crime drama television series created and 152 | primarily written by American author and former police reporter David 153 | Simon. The series was broadcast by the cable network HBO in the United 154 | States. The Wire premiered on June 2, 2002, and ended on March 9, 2008, 155 | comprising sixty episodes over five seasons. The idea for the show 156 | started out as a police drama loosely based on the experiences of 157 | Simon's writing partner Ed Burns, a former homicide detective and 158 | public school teacher. 159 | note: The Wire is noted to be solid 160 | genre: Drama 161 | affiliated: 162 | - role: executive-producer 163 | names: ["Simon, David", "Colesberry, Robert F.", "Noble, Nina Kostroff"] 164 | 165 | kinetics: 166 | type: Article 167 | title: Kinetics and luminescence of the excitations of a nonequilibrium polariton condensate 168 | author: ["Doan, T. D.", "Tran Thoai, D. B.", "Haug, Hartmut"] 169 | serial-number: 170 | doi: 10.1103/PhysRevB.102.165126 171 | page-total: 13 172 | page-range: 165126-165139 173 | date: 2020-10-14 174 | parent: 175 | title: 176 | value: Physical Review B 177 | verbatim: true 178 | volume: 102 179 | issue: 16 180 | publisher: American Physical Society 181 | 182 | house: 183 | type: Article 184 | title: Teaching medicine with the help of "Dr. House" 185 | author: ["Jerrentrup, Andreas", "Mueller, Tobias", "Glowalla, Ulrich", "Herder, Meike", "Henrichs, Nadine", "Neubauer, Andreas", "Schaefer, Juergen R."] 186 | serial-number: 187 | doi: 10.1371/journal.pone.0193972 188 | serial: e0193972 189 | date: 2018-03-13 190 | parent: 191 | title: 192 | value: PLoS ONE 193 | verbatim: true 194 | volume: 13 195 | issue: 3 196 | 197 | plaque: 198 | type: Misc 199 | title: Informational plaque about Jacoby's 1967 photos 200 | publisher: Stiftung Reinbeckhallen 201 | location: Berlin, Germany 202 | date: 2020 203 | parent: 204 | type: Artwork 205 | date: 1967 206 | author: Jacoby, Max 207 | parent: 208 | type: Anthology 209 | title: Bleibtreustraße 210 | archive: Landesmuseum Koblenz 211 | archive-location: Koblenz, Germany 212 | 213 | oiseau: 214 | type: Exhibition 215 | title: L'oiseau rare, de l'hirondelle au kakapo 216 | date: 2020-12-18 217 | organization: Musée des Confluences 218 | location: Lyon, France 219 | url: 220 | value: https://www.museedesconfluences.fr/fr/evenements/l%E2%80%99oiseau-rare-de-l%E2%80%99hirondelle-au-kakapo 221 | date: 2020-11-04 222 | 223 | renaissance: 224 | type: Book 225 | title: 226 | value: Renaissance, Les Déracinés 227 | translation: Renaissance, The Unrooted 228 | language: fr-FR 229 | author: Duval, Fred 230 | affiliated: 231 | - role: illustrator 232 | names: ["Emem", "Blanchard, Fred"] 233 | date: 2018 234 | publisher: Dargaud 235 | edition: 1 236 | volume: 1 237 | volume-total: 3 238 | 239 | gedanken: 240 | type: Anthos 241 | title: Gedanken-experiments on sequential machines 242 | author: Moore, Edward F. 243 | page-range: 129-153 244 | parent: 245 | type: Anthology 246 | title: Automata studies 247 | editor: ["Shannon, C. E.", "McCarthy, J."] 248 | volume: 34 249 | date: 1956-04 250 | serial-number: { isbn: 978-0-691-07916-5 } 251 | location: Princeton, NJ, USA 252 | publisher: NBS 253 | parent: 254 | type: Anthology 255 | title: Annals of Mathematics Studies 256 | 257 | georgia: 258 | type: post 259 | author: Silver, Nate 260 | title: Trump's claim to have won Georgia is highly dubious. No network has called it. He's only ahead by 2.5 points there, and the outstanding votes are mostly mail votes in very blue counties, likely very Democratic. Biden may even be a slight favorite there. 261 | url: https://twitter.com/NateSilver538/status/1323889051037028353 262 | date: 2020-11-04 263 | 264 | really-habitable: 265 | type: Article 266 | title: Defining the Really Habitable Zone 267 | author: ["Pedbost, Marven F.", "Pomalgu, Trillean", "Lintott, Chris", "Eisner, Nora", "Nicholson, Belinda"] 268 | date: 2020 269 | serial-number: 2003.13722 270 | url: https://arxiv.org/abs/2003.13722 271 | parent: 272 | type: Repository 273 | title: arXiv 274 | 275 | electronic-music: 276 | type: Web 277 | title: Ishkur's Guide to Electronic Music 278 | serial-number: v2.5 279 | author: Ishkur 280 | url: { value: http://www.techno.org/electronic-music-guide/, date: 2020-11-12 } 281 | 282 | mattermost: 283 | type: Web 284 | title: Mattermost Privacy Policy 285 | author: Mattermost 286 | url: 287 | value: https://mattermost.com/privacy-policy/ 288 | date: 2020-11-29 289 | parent: 290 | type: Web 291 | title: Policies 292 | 293 | worth: 294 | type: Blog 295 | title: Jon Worth Euroblog 296 | author: Worth, Jon 297 | url: https://jonworth.eu/ 298 | 299 | wrong: 300 | type: Article 301 | title: It is fast or it is wrong 302 | date: 2018-12-29 303 | author: Prokopov, Nikita 304 | url: https://tonsky.me/blog/slow-wrong/ 305 | parent: 306 | type: Blog 307 | title: tonsky.me 308 | 309 | un-hdr: 310 | type: Report 311 | author: United Nations Development Programme 312 | title: Human Development Report 2019 313 | location: New York 314 | serial-number: { issn: 2412-3129 } 315 | url: http://hdr.undp.org/sites/default/files/hdr2019.pdf 316 | date: 2019 317 | 318 | audio-descriptions: 319 | type: Audio 320 | title: Audio Descriptions 321 | issue: 8 322 | date: 2017-02-07 323 | author: Barrows, Miellyn Fitzwater 324 | affiliated: 325 | - names: Taylor, Dallas 326 | role: narrator 327 | url: https://www.20k.org/episodes/audio 328 | parent: 329 | type: Audio 330 | title: Twenty Thousand Hertz 331 | author: Taylor, Dallas 332 | 333 | drill: 334 | type: Artwork 335 | title: Drill 336 | author: Steyerl, Hito 337 | date: 2019-06-20 338 | location: New York 339 | organization: Thompson Arts Center 340 | 341 | camb: 342 | type: Entry 343 | title: authoritative 344 | url: 345 | value: https://dictionary.cambridge.org/dictionary/english/authoritative 346 | date: 2020-11-29 347 | parent: 348 | type: Reference 349 | title: Cambridge Dictionary 350 | 351 | logician: 352 | type: Entry 353 | title: Logician 354 | serial-number: n10269785 355 | url: 356 | value: http://image-net.org/api/text/wordnet.structure.hyponym?wnid=n10269785 357 | date: 2019-12-02 358 | parent: 359 | type: Repository 360 | title: ImageNet 361 | url: http://image-net.org 362 | 363 | dns-encryption: 364 | type: Reference 365 | title: Secret Key Transaction Authentication for DNS 366 | serial-number: RFC 2845 367 | author: Internet Engineering Task Force 368 | url: https://tools.ietf.org/html/rfc2845 369 | date: 2000 370 | 371 | roe-wade: 372 | type: Case 373 | title: Roe v. Wade 374 | serial-number: 93 S. Ct. 705 375 | date: 1973 376 | 377 | foia: 378 | type: Legislation 379 | title: Freedom of Information Act 380 | serial-number : Pub. L. No. 107-296, 80 Stat. 250 381 | date: 1967 382 | 383 | overleaf: 384 | type: Article 385 | title: Celebrating over five million users, a quarter million daily actives, and over five years of dedicated user support 386 | date: 2019-11-08 387 | author: John 388 | url: https://de.overleaf.com/blog/celebrating-over-five-million-users-and-a-quarter-million-daily-actives 389 | parent: 390 | type: Blog 391 | title: Overleaf Blog 392 | 393 | latex-users: 394 | type: Article 395 | title: How many scholarly articles are written in LaTeX? 396 | serial-number: 397 | doi: 10.22541/au.148771883.35456290 398 | author: Pepe, Alberto 399 | date: 2017-02-21 400 | parent: 401 | type: Repository 402 | title: Authorea 403 | 404 | editors: 405 | type: Artwork 406 | title: Types of Editors 407 | author: Munroe, Randall 408 | date: 2014-03-12 409 | url: https://xkcd.com/1341/ 410 | 411 | barb: 412 | type: Book 413 | title: Den Boden unter den Füßen verlieren 414 | author: Günther-Haug, Barbara 415 | date: 2020 416 | publisher: MVG 417 | location: München 418 | language: de-DE 419 | 420 | lamb: 421 | type: Book 422 | title: The life of a friendly lamb 423 | author: Kell-Nehrer, Felips 424 | date: 2011 425 | chapter: 20 426 | volume: 10 427 | page-range: 22-23 428 | tongus: 2 429 | 430 | snail: 431 | type: Book 432 | title: A Snail Study 433 | author: Kell-Nehrer, Felips 434 | date: 2011 435 | chapter: 2A 436 | volume: 4B 437 | page-range: 28 438 | 439 | lamb-chapter: 440 | type: Chapter 441 | page-range: 10-12 442 | chapter: 3 443 | parent: 444 | type: Book 445 | title: The life of a friendly lamb 446 | author: Kell-Nehrer, Felips 447 | date: 2011 448 | chapter: 20 449 | volume: 10 450 | page-range: 22-23 451 | tongus: 2 452 | 453 | snail-chapter: 454 | type: Chapter 455 | page-range: 10-12 456 | serial-number: 3 457 | parent: 458 | type: Book 459 | title: A Snail Study 460 | author: Kell-Nehrer, Felips 461 | date: 2011 462 | volume: 4B 463 | page-range: 28 464 | --------------------------------------------------------------------------------