Skip to content

Commit a713031

Browse files
committed
Go nuts on RDFa
1 parent bb5489e commit a713031

File tree

7 files changed

+238
-93
lines changed

7 files changed

+238
-93
lines changed

.vnurc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ The “searchbox” role is unnecessary for an “input” element that has no
1010
Text run is not in Unicode Normalization Form C.
1111
The value of attribute “.*” on element “.*” from namespace “http://www.w3.org/1999/xhtml” is not in Unicode Normalization Form C.
1212
# Years <1000 are ok
13-
Potentially bad value “0...” for attribute “datetime” on element “time”: Year may be mistyped.
13+
Potentially bad value “0...” for attribute “datetime” on element “time”.*

build.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#!/bin/env pwsh
2+
#Requires -Version 7.2
23
param(
34
[switch]$watch = $false,
45
[switch]$drafts = $false,
56
[switch]$skip_build = $false
67
)
78

89
$ErrorActionPreference = 'Stop'
10+
$PSNativeCommandUseErrorActionPreference = $true
911

1012
$env:LC_ALL = "C.UTF-8"
1113
$env:MAGICK_THREAD_LIMIT = 1

build/src/bib_render.rs

Lines changed: 134 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use time::macros::format_description;
55

66
use crate::{
77
bibliography::{
8-
Bibliography, Book, Chapter, Common, ConferencePaper, Date, Document, JournalArticle,
9-
LString, MagazineArticle, NewspaperArticle, NumberOrString, Pagination, Periodical, Person,
10-
Reference, Thesis, WebPage,
8+
identifier_info, Bibliography, Book, Chapter, Common, ConferencePaper, Date, Document,
9+
JournalArticle, LString, MagazineArticle, NewspaperArticle, NumberOrString, Pagination,
10+
Periodical, Person, Reference, Thesis, WebPage,
1111
},
1212
intl::INTL,
1313
nvec::OneOrMore,
@@ -27,8 +27,26 @@ pub type InlineCiteRenderer = Box<dyn Fn(&str, Option<Markup>) -> Markup + Send
2727

2828
pub type RenderedBibliography = BTreeMap<String, RenderedEntry>;
2929

30+
fn backfill_isbn(reference: &Reference) -> Cow<'_, Reference> {
31+
match reference {
32+
Reference::Book(Book {
33+
isbn: Some(isbn), ..
34+
}) => {
35+
let mut result = reference.clone();
36+
result
37+
.common_mut()
38+
.identifiers
39+
.insert(format!("isbn:{}", isbn.0));
40+
return Cow::Owned(result);
41+
}
42+
_ => {}
43+
}
44+
45+
Cow::Borrowed(reference)
46+
}
47+
3048
fn backfill_doi(reference: &Reference) -> Cow<'_, Reference> {
31-
if reference.doi().is_none() {
49+
if reference.identifier("doi:").is_none() {
3250
if let Some(url) = reference.common().url.as_deref() {
3351
if let Some(doi) = url.strip_prefix("https://doi.org/") {
3452
let mut r = reference.clone();
@@ -42,7 +60,7 @@ fn backfill_doi(reference: &Reference) -> Cow<'_, Reference> {
4260
}
4361

4462
fn backfill_handle(reference: &Reference) -> Cow<'_, Reference> {
45-
if reference.hdl().is_none() {
63+
if reference.identifier("hdl:").is_none() {
4664
if let Some(url) = reference.common().url.as_deref() {
4765
if let Some(hdl) = url.strip_prefix("https://hdl.handle.net/") {
4866
// n2t doesn't handle handles with query strings
@@ -84,6 +102,7 @@ pub fn to_rendered(bib: &Bibliography) -> RenderedBibliography {
84102
.unwrap_or_default();
85103

86104
let reference = backfill_doi(reference);
105+
let reference = backfill_isbn(&reference);
87106
let reference = backfill_handle(&reference);
88107
let reference = render_ref(key, &reference);
89108

@@ -166,40 +185,46 @@ fn inline_cite(reference: &Reference) -> Option<InlineCiteRenderer> {
166185

167186
fn book_item_type(book: &Book) -> &'static str {
168187
if book.volume.is_none() {
169-
"Book bibo:Book"
188+
"Book bibo:Book fabio:Book"
170189
} else {
171-
"Book bibo:Book PublicationVolume"
172-
}
173-
}
174-
175-
fn thesis_item_type(thesis: &Thesis) -> &'static str {
176-
if thesis.volume.is_none() {
177-
"Thesis bibo:Thesis"
178-
} else {
179-
"Thesis bibo:Thesis PublicationVolume"
190+
"Book PublicationVolume bibo:Book fabio:Book"
180191
}
181192
}
182193

183194
fn item_type(reference: &Reference) -> &'static str {
184195
match reference {
185196
Reference::ConferencePaper(_) => "ScholarlyArticle bibo:AcademicArticle",
186197
Reference::JournalArticle(_) => "ScholarlyArticle bibo:AcademicArticle",
187-
Reference::NewspaperArticle(_) | Reference::MagazineArticle(_) => "Article",
188-
Reference::Book(b) => book_item_type(b),
189-
Reference::Thesis(t) => thesis_item_type(t),
190-
Reference::Chapter(_) => "Chapter bibo:Chapter",
191-
Reference::Patent(_) => "CreativeWork bibo:Patent",
192-
Reference::Document(_) => "CreativeWork bibo:Document",
193-
Reference::WebPage(_) => "WebPage bibo:Webpage",
198+
Reference::NewspaperArticle(_) => "Article bibo:Article fabio:NewspaperArticle",
199+
Reference::MagazineArticle(_) => "Article bibo:Article fabio:MagazineArticle",
200+
Reference::Book(b) => {
201+
if b.volume.is_none() {
202+
"Book bibo:Book fabio:Book"
203+
} else {
204+
"Book PublicationVolume bibo:Book fabio:Book"
205+
}
206+
}
207+
Reference::Thesis(t) => {
208+
if t.volume.is_none() {
209+
"Thesis bibo:Thesis fabio:Thesis"
210+
} else {
211+
"Thesis PublicationVolume bibo:Thesis fabio:Thesis"
212+
}
213+
}
214+
Reference::Chapter(_) => "Chapter bibo:Chapter fabio:BookChapter",
215+
Reference::Patent(p) => {
216+
if p.issued.is_some() {
217+
"CreativeWork bibo:Patent fabio:Patent"
218+
} else {
219+
"CreativeWork bibo:Patent fabio:PatentApplication"
220+
}
221+
}
222+
Reference::Document(_) => "CreativeWork bibo:Document fabio:Work",
223+
Reference::WebPage(_) => "WebPage bibo:Webpage fabio:WebPage",
194224
}
195225
}
196226

197227
fn item_resource(reference: &Reference) -> Option<String> {
198-
if let Some(doi) = reference.doi() {
199-
// DOI can be treated as a URN, since it is registered
200-
return Some(format!("urn:{doi}"));
201-
}
202-
203228
match reference {
204229
// Reference books with ISBNs via URN
205230
Reference::Book(Book {
@@ -235,9 +260,8 @@ fn render_ref(key: &str, reference: &Reference) -> Markup {
235260
(render_container(key, reference))
236261
(render_genre(reference))
237262
(render_publisher(key, reference))
238-
(render_isbn(reference))
239-
(render_original(reference))
240263
(render_identifiers(reference))
264+
(render_original(reference))
241265
}
242266
}
243267
}
@@ -539,7 +563,7 @@ fn render_date(reference: &Reference) -> Markup {
539563
abbr title="circa" { "c." }
540564
" "
541565
}
542-
(date.year().to_string())
566+
span property="fabio:hasPublicationYear" { (date.year().to_string()) }
543567
@if date.attr().old_style {
544568
" ["
545569
abbr title="old-style" { "OS" }
@@ -670,8 +694,8 @@ fn render_series(r: &Reference) -> Markup {
670694

671695
html! {
672696
"; "
673-
span property="isPartOf" typeof="BookSeries" {
674-
@let title = render_lstr(&series.title, None, Some("name"), Some("alternateName"));
697+
span property="isPartOf dcterms:isPartOf frbr:partOf" typeof="BookSeries bibo:Series fabio:BookSeries" {
698+
@let title = render_lstr(&series.title, None, Some("name dcterms:title"), Some("alternateName"));
675699
@if let Some(url) = &series.url {
676700
a property="url" href=(url) { (title) }
677701
} @else {
@@ -681,7 +705,7 @@ fn render_series(r: &Reference) -> Markup {
681705
" ("
682706
abbr.initialism { "ISSN" }
683707
" "
684-
span property="issn" { (issn.to_string(false)) }
708+
span property="issn bibo:issn" { (issn.to_string(false)) }
685709
")"
686710
}
687711
@if let Some(volume) = &series.volume {
@@ -736,8 +760,16 @@ fn render_container(key: &str, r: &Reference) -> Markup {
736760
| Reference::MagazineArticle(MagazineArticle {
737761
page, periodical, ..
738762
}) => {
763+
let pt = if matches!(r, Reference::JournalArticle(_)) {
764+
PeriodicalType::Journal
765+
} else if matches!(r, Reference::NewspaperArticle(_)) {
766+
PeriodicalType::NewsPaper
767+
} else {
768+
PeriodicalType::Magazine
769+
};
770+
739771
html! {
740-
(render_periodical(key, periodical))
772+
(render_periodical(key, periodical, pt))
741773
@if let Some(page) = page {
742774
@if matches!(page, Pagination::Num(_)) {
743775
": page "
@@ -789,7 +821,7 @@ fn render_container(key: &str, r: &Reference) -> Markup {
789821
}) => html! {
790822
@if let Some(title) = container_title {
791823
"On the website "
792-
span property="isPartOf dcterms:isPartOf" typeof="WebSite bibo:Website"{
824+
span property="isPartOf dcterms:isPartOf frbr:partOf" typeof="WebSite bibo:Website fabio:WebSite"{
793825
(render_lstr_cite(title, None, Some("name dcterms:title"), Some("alternateName")))
794826
}
795827

@@ -801,7 +833,7 @@ fn render_container(key: &str, r: &Reference) -> Markup {
801833
@let iso_date = access_date.format(format_description!("[year]-[month]-[day]")).unwrap();
802834
@let nice_date = format!("{}, {} {} {}", access_date.weekday(), ordinal(access_date.day() as u64), access_date.month(), access_date.year());
803835
" (accessed "
804-
time property="lastReviewed" datetime=(iso_date) { (nice_date) }
836+
time property="lastReviewed fabio:hasDepositDate" datetime=(iso_date) { (nice_date) }
805837
")"
806838
}
807839

@@ -820,7 +852,7 @@ fn render_book(key: &str, book: &Book, item_prop: &str) -> Markup {
820852
let bookr = &Reference::Book(book.clone());
821853
let key = key.to_string() + "-book";
822854
html! {
823-
span property=(item_prop) typeof=(book_item_type(book)) {
855+
span property=(item_prop) typeof=(book_item_type(book)) resource=[bookr.common().resource_id()] {
824856
(render_title(bookr))
825857
@if let Ok(authors) = bookr.authors().try_into() {
826858
", " (render_people(&authors, false, "author"))
@@ -836,7 +868,13 @@ fn render_book(key: &str, book: &Book, item_prop: &str) -> Markup {
836868
}
837869
}
838870

839-
fn render_periodical(key: &str, p: &Periodical) -> Markup {
871+
enum PeriodicalType {
872+
Journal,
873+
NewsPaper,
874+
Magazine,
875+
}
876+
877+
fn render_periodical(key: &str, p: &Periodical, pt: PeriodicalType) -> Markup {
840878
let date_part = if matches!(p.issued, Date::YearMonthDay { .. } | Date::YearMonth { .. }) {
841879
html! {
842880
", "
@@ -848,56 +886,82 @@ fn render_periodical(key: &str, p: &Periodical) -> Markup {
848886
Markup::default()
849887
};
850888

889+
let periodical_resource = if let Some(issn) = &p.issn {
890+
format!("urn:issn:{}", issn.to_string(false))
891+
} else {
892+
format!("#{key}-periodical")
893+
};
894+
895+
let periodical_type = match pt {
896+
PeriodicalType::Journal => "Periodical bibo:Journal fabio:Journal",
897+
PeriodicalType::NewsPaper => "Periodical bibo:Newspaper fabio:Newspaper",
898+
PeriodicalType::Magazine => "Periodical bibo:Magazine fabio:Magazine",
899+
};
900+
901+
let volume_type = match pt {
902+
PeriodicalType::Journal => "PublicationVolume bibo:CollectedDocument fabio:JournalVolume",
903+
PeriodicalType::NewsPaper => {
904+
"PublicationVolume bibo:CollectedDocument fabio:NewspaperVolume"
905+
}
906+
PeriodicalType::Magazine => "PublicationVolume bibo:CollectedDocument fabio:MagazineVolume",
907+
};
908+
909+
let issue_type = match pt {
910+
PeriodicalType::Journal => "PublicationIssue bibo:Issue fabio:JournalIssue",
911+
PeriodicalType::NewsPaper => "PublicationIssue bibo:Issue fabio:NewspaperIssue",
912+
PeriodicalType::Magazine => "PublicationIssue bibo:Issue fabio:MagazineIssue",
913+
};
914+
851915
html! {
852916
@match (p.issue, &p.volume) {
853917
(Some(issue), Some(volume)) => {
854-
span typeof="Periodical" resource={"#"(key)"-periodical"} {
855-
link property="publisher" href={"#"(key)"-publisher"};
918+
span typeof=(periodical_type) resource=(periodical_resource) {
919+
link property="publisher dc:publisher" href={"#"(key)"-publisher"};
856920
(render_lstr_cite(&p.title, None, Some("name"), Some("alternateName")))
857921
}
858922
" "
859-
span typeof="PublicationVolume" resource={"#"(key)"-volume"} {
860-
link property="isPartOf" href={"#"(key)"-periodical"};
923+
span typeof=(volume_type) resource={"#"(key)"-volume"} {
924+
link property="isPartOf frbr:partOf" href=(periodical_resource);
861925
abbr title="volume" { "vol." }
862926
" "
863-
span property="volumeNumber" { (volume.to_string(true)) }
927+
span property="volumeNumber bibo:volume" { (volume.to_string(true)) }
864928
}
865929
" "
866-
span typeof="PublicationIssue" property="isPartOf" {
867-
link property="isPartOf" href={"#"(key)"-volume"};
868-
"(" span property="issueNumber" { (INTL.format_number(issue)) } ")"
930+
span typeof=(issue_type) property="isPartOf frbr:partOf" {
931+
link property="isPartOf frbr:partOf" href={"#"(key)"-volume"};
932+
"(" span property="issueNumber bibo:issue" { (INTL.format_number(issue)) } ")"
869933
(date_part)
870934
}
871935
}
872936
(Some(issue), None) => {
873-
span typeof="Periodical" resource={"#"(key)"-periodical"} {
874-
link property="publisher" href={"#"(key)"-publisher"};
937+
span typeof=(periodical_type) resource=(periodical_resource) {
938+
link property="publisher dc:publisher" href={"#"(key)"-publisher"};
875939
(render_lstr_cite(&p.title, None, Some("name"), Some("alternateName")))
876940
}
877941
" "
878-
span typeof="PublicationIssue" property="isPartOf" {
879-
link property="isPartOf" href={"#"(key)"-periodical"};
880-
"(" span property="issueNumber" { (INTL.format_number(issue)) } ")"
942+
span typeof=(issue_type) property="isPartOf frbr:partOf" {
943+
link property="isPartOf frbr:partOf" href=(periodical_resource);
944+
"(" span property="issueNumber bibo:issue" { (INTL.format_number(issue)) } ")"
881945
(date_part)
882946
}
883947
}
884948
(None, Some(volume)) => {
885-
span typeof="Periodical" resource={"#"(key)"-periodical"} {
886-
link property="publisher" href={"#"(key)"-publisher"};
949+
span typeof=(periodical_type) resource=(periodical_resource) {
950+
link property="publisher dc:publisher" href={"#"(key)"-publisher"};
887951
(render_lstr_cite(&p.title, None, Some("name"), Some("alternateName")))
888952
}
889953
" "
890-
span typeof="PublicationVolume" property="isPartOf" {
891-
link property="isPartOf" href={"#"(key)"-periodical"};
954+
span typeof=(volume_type) property="isPartOf frbr:partOf" {
955+
link property="isPartOf" href=(periodical_resource);
892956
abbr title="volume" { "vol." }
893957
" "
894-
span property="volumeNumber" { (volume.to_string(true)) }
958+
span property="volumeNumber bibo:volume" { (volume.to_string(true)) }
895959
(date_part)
896960
}
897961
}
898962
(None, None) => {
899-
span typeof="Periodical" property="isPartOf" {
900-
link property="publisher" href={"#"(key)"-publisher"};
963+
span typeof=(periodical_type) resource=(periodical_resource) property="isPartOf frbr:partOf" {
964+
link property="publisher dc:publisher" href={"#"(key)"-publisher"};
901965
(render_lstr_cite(&p.title, None, Some("name"), Some("alternateName")))
902966
(date_part)
903967
}
@@ -1019,10 +1083,19 @@ fn render_isbn(r: &Reference) -> Markup {
10191083
fn render_identifiers(r: &Reference) -> Markup {
10201084
html! {
10211085
@for id in &r.common().identifiers {
1086+
@let info = identifier_info(id);
10221087
" "
1023-
span.id {
1024-
a property="sameAs" href={"https://n2t.net/"(id)} {
1025-
span property="dcterms:identifier" { (id) }
1088+
@if info.digital {
1089+
span.id {
1090+
a property="sameAs" href=(info.url) {
1091+
span property=(info.property) { (info.presentation_form.as_deref().unwrap_or(id)) }
1092+
}
1093+
}
1094+
} @else {
1095+
@let (prefix, suffix) = id.split_once(':').unwrap();
1096+
abbr.initialism { (prefix.to_ascii_uppercase()) } ": "
1097+
a.isbn property="sameAs" href=(info.url) {
1098+
span property=(info.property) { (info.presentation_form.as_deref().unwrap_or(suffix)) }
10261099
}
10271100
}
10281101
". "

0 commit comments

Comments
 (0)