diff --git a/src/decode.go b/src/decode.go new file mode 100644 index 0000000..53cebe4 --- /dev/null +++ b/src/decode.go @@ -0,0 +1,55 @@ +package main + +import ( + "log" + "strings" +) + +type conversion struct { + from string + to string +} + +func decodeTitle(title string) string { + for _, convert := range []conversion{ + {`\#`, "#"}, + {`--`, `–`}, + {"``", "“"}, + {"''", "”"}, + {"'", "’"}, // U+2019 + {`$\cdot$`, `·`}, // U+00B7. + } { + title = strings.Replace(title, convert.from, convert.to, -1) + } + + // Get rid of all curly brackets. We're displaying titles without changing + // their casing. + title = strings.ReplaceAll(title, "{", "") + title = strings.ReplaceAll(title, "}", "") + + return title +} + +func decodeAuthors(authors string) string { + for _, convert := range []conversion{ + {"'", "’"}, + } { + authors = strings.Replace(authors, convert.from, convert.to, -1) + } + // For simplicity, we expect authors to be formatted as "John Doe" instead + // of "Doe, John". + if strings.Contains(authors, ",") { + log.Fatalf("author %q contains a comma", authors) + } + authorSlice := strings.Split(authors, " and ") + return strings.Join(authorSlice, ", ") +} + +func decodeProceedings(proceedings string) string { + for _, convert := range []conversion{ + {`\&`, "&"}, + } { + proceedings = strings.Replace(proceedings, convert.from, convert.to, -1) + } + return proceedings +} diff --git a/src/decode_test.go b/src/decode_test.go new file mode 100644 index 0000000..8276555 --- /dev/null +++ b/src/decode_test.go @@ -0,0 +1,81 @@ +package main + +import ( + "testing" +) + +func TestToString(t *testing.T) { + testCases := []conversion{ + { + from: "Title", + to: "Title", + }, + { + from: "This is a {Title}", + to: "This is a Title", + }, + { + from: "This is a {Title}", + to: "This is a Title", + }, + { + from: `{\#h00t}: Censorship Resistant Microblogging`, + to: `#h00t: Censorship Resistant Microblogging`, + }, + { + from: "``Good'' Worms and Human Rights", + to: "“Good” Worms and Human Rights", + }, + { + from: "An Analysis of {China}'s ``{Great Cannon}''", + to: "An Analysis of China’s “Great Cannon”", + }, + { + from: `lib$\cdot$erate, (n):`, + to: `lib·erate, (n):`, + }, + { + from: "Well -- Exploring the {Great} {Firewall}'s Poisoned {DNS}", + to: "Well – Exploring the Great Firewall’s Poisoned DNS", + }, + } + + for _, test := range testCases { + to := decodeTitle(test.from) + if to != test.to { + t.Errorf("Expected\n%s\ngot\n%s", test.to, to) + } + } +} + +func TestDecodeAuthors(t *testing.T) { + testCases := []conversion{ + { // Multiple authors should be separated by commas. + from: "John Doe and Jane Doe", + to: "John Doe, Jane Doe", + }, + { // Single authors should remain as-is. + from: "John Doe", + to: "John Doe", + }, + { // Single-name authors should remain as-is. + from: "John and Jane", + to: "John, Jane", + }, + { // Non-ASCII characters should be unaffected. + from: "Jóhn Doe", + to: "Jóhn Doe", + }, + { // Apostrophes should be replaced with the right single quote. + from: "John O'Brian", + to: "John O’Brian", + }, + } + + for _, test := range testCases { + to := decodeAuthors(test.from) + if to != test.to { + t.Errorf("Expected\n%s\ngot\n%s", test.to, to) + } + } +} diff --git a/src/footer.go b/src/footer.go new file mode 100644 index 0000000..796c9e1 --- /dev/null +++ b/src/footer.go @@ -0,0 +1,11 @@ +package main + +func footer() string { + return `
+ +