From 0d0de9e8f0a6a3b03703655f880f9eb4713878b2 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Wed, 5 Feb 2025 10:12:42 +0100 Subject: [PATCH] Add conversions for movie-related roles This fixes issue #266. Other than what the title of the issue suggests, `PersonRole::Unknown` can still not be serialized, deciding [on this](https://github.com/typst/hayagriva/issues/266#issuecomment-2633418457) is left to a later PR. Instead, the PR adds some conversions for editor types the biblatex manual does not specify. Support for the `scriptwriter` type is justified by [zotero-better-biblatex's output](https://github.com/retorquere/zotero-better-bibtex/blob/c617185750df065f597a54b447a5b71f690777e4/test/fixtures/export/Exporting%20item%20type%20film%20merges%20scriptwriter%20with%20other%20contributors%20%232802.biblatex#L8). Support for the `producer` type is based on use in both [`biblatex-chicago`](https://markov.htwsaar.de/tex-archive/macros/latex/contrib/biblatex-contrib/biblatex-chicago/doc/biblatex-chicago.pdf) and [`biblatex-apa`](https://ctan.net/macros/latex/contrib/biblatex-contrib/biblatex-apa/biblatex-apa-test-references.bib). `biblatex-apa` uses `writer` instead of `screenwriter`, so that's supported instead. A peculiarity is that `biblatex-chicago` gives the value `none` the semantic meaning of performers within `video` and `music` entries (see e.g. its manual p. 27). This value is chosen to hack BibLaTeX into not printing any label. --- src/interop.rs | 83 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/src/interop.rs b/src/interop.rs index 7c31a36..32b1cd9 100644 --- a/src/interop.rs +++ b/src/interop.rs @@ -115,7 +115,7 @@ impl From<&PermissiveType> for MaybeTyped { } } -fn ed_role(role: EditorType) -> Option { +fn ed_role(role: EditorType, entry_type: &tex::EntryType) -> Option { match role { EditorType::Editor => None, EditorType::Compiler => Some(PersonRole::Compiler), @@ -126,7 +126,25 @@ fn ed_role(role: EditorType) -> Option { EditorType::Collaborator => Some(PersonRole::Collaborator), EditorType::Organizer => Some(PersonRole::Organizer), EditorType::Director => Some(PersonRole::Director), - EditorType::Unknown(role) => Some(PersonRole::Unknown(role)), + EditorType::Unknown(role) => { + let other_entry_type = if let tex::EntryType::Unknown(t) = entry_type { + Some(t.to_ascii_lowercase()) + } else { + None + }; + + match (role.to_ascii_lowercase().as_str(), other_entry_type.as_deref()) { + // See p. 26 of the biblatex-chicago manual and biblatex-apa + ("producer", _) => Some(PersonRole::Producer), + // The pervasive Zotero plugin zotero-better-biblatex produces this. + ("scriptwriter", _) => Some(PersonRole::Writer), + // The biblatex-apa style expects `writer` for videos. + ("writer", Some("video")) => Some(PersonRole::Writer), + // See p. 26 of the biblatex-chicago manual + ("none", Some("video") | Some("music")) => Some(PersonRole::CastMember), + _ => Some(PersonRole::Unknown(role)), + } + } } } @@ -209,7 +227,7 @@ impl TryFrom<&tex::Entry> for Entry { let mut eds: Vec = vec![]; let mut collaborators = vec![]; for (editors, role) in entry.editors()? { - let ptype = ed_role(role); + let ptype = ed_role(role, &entry.entry_type); match ptype { None => eds.extend(editors.iter().map(Into::into)), Some(role) => collaborators.push(PersonsWithRoles::new( @@ -586,24 +604,63 @@ fn comma_list(items: &[Vec>]) -> FormatString { #[cfg(test)] mod tests { + use crate::types::PersonRole; + #[test] fn test_pmid_from_biblatex() { let entries = crate::io::from_biblatex_str( r#"@article{test_article, - title = {Title}, - volume = {3}, - url = {https://example.org}, - pages = {1--99}, - journaltitle = {Testing Journal}, - author = {Doe, Jane}, - date = {2024-12}, - eprint = {54678}, - eprinttype = {pubmed}, -}"#, + title = {Title}, + volume = {3}, + url = {https://example.org}, + pages = {1--99}, + journaltitle = {Testing Journal}, + author = {Doe, Jane}, + date = {2024-12}, + eprint = {54678}, + eprinttype = {pubmed}, + }"#, ) .unwrap(); let entry = entries.get("test_article").unwrap(); assert_eq!(Some("54678"), entry.keyed_serial_number("pmid")); assert_eq!(Some("54678"), entry.pmid()); } + + #[test] + /// See https://github.com/typst/hayagriva/issues/266 + fn issue_266() { + let entries = crate::io::from_biblatex_str( + r#"@video{wachowskiMatrix1999, + type = {Action, Sci-Fi}, + entrysubtype = {film}, + title = {The {{Matrix}}}, + editor = {Wachowski, Lana and Wachowski, Lilly}, + editortype = {director}, + editora = {Wachowski, Lilly and Wachowski, Lana}, + editoratype = {scriptwriter}, + namea = {Reeves, Keanu and Fishburne, Laurence and Moss, Carrie-Anne}, + nameatype = {collaborator}, + date = {1999-03-31}, + publisher = {Warner Bros., Village Roadshow Pictures, Groucho Film Partnership}, + abstract = {When a beautiful stranger leads computer hacker Neo to a forbidding underworld, he discovers the shocking truth--the life he knows is the elaborate deception of an evil cyber-intelligence.}, + keywords = {artificial reality,dystopia,post apocalypse,simulated reality,war with machines}, + annotation = {IMDb ID: tt0133093\\ + event-location: United States, Australia} + }"#, + ).unwrap(); + + let entry = entries.get("wachowskiMatrix1999").unwrap(); + assert_eq!( + Some("Lilly"), + entry + .affiliated_with_role(PersonRole::Writer) + .first() + .unwrap() + .given_name + .as_deref() + ); + + serde_json::to_value(entry).unwrap(); + } }