diff options
Diffstat (limited to 'internal/render/ascii.go')
| -rw-r--r-- | internal/render/ascii.go | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/internal/render/ascii.go b/internal/render/ascii.go new file mode 100644 index 0000000..4203bc9 --- /dev/null +++ b/internal/render/ascii.go @@ -0,0 +1,67 @@ +package render + +import "strings" + +// asciiMap is deliberately one rune to one ASCII character. +// +// Substitution happens after the table has been laid out, so anything that +// changed the number of characters would shear every column to its right. That +// constraint is why the wind arrows become single letters rather than the +// two-letter compass points that would read better. +var asciiMap = map[rune]string{ + // Polish diacritics, so a Polish forecast survives GSM-7. + 'ą': "a", 'ć': "c", 'ę': "e", 'ł': "l", 'ń': "n", + 'ó': "o", 'ś': "s", 'ź': "z", 'ż': "z", + 'Ą': "A", 'Ć': "C", 'Ę': "E", 'Ł': "L", 'Ń': "N", + 'Ó': "O", 'Ś': "S", 'Ź': "Z", 'Ż': "Z", + + // Wind arrows. The arrow points the way the wind blows, so v is a northerly. + '↑': "^", '↓': "v", '←': "<", '→': ">", + '↖': "\\", '↗': "/", '↙': "/", '↘': "\\", + + // Chart: blocks and the axis rule. + '█': "#", '▇': "#", '▆': "=", '▅': "=", + '▄': "_", '▃': ":", '▂': ".", '▁': ".", + '│': "|", +} + +// ASCII rewrites output to pure ASCII, one character for one character. +// +// The point is SMS: a single non-ASCII character forces the whole message from +// GSM-7 into UCS-2, which cuts a segment from 160 characters to 70. A degree +// sign alone therefore more than doubles the cost of sending a forecast. +// +// The degree sign becomes the unit letter, which is both ASCII and clearer to +// someone reading it cold: "29C" rather than "29". +func ASCII(s, units string) string { + degree := "C" + if units == "imperial" { + degree = "F" + } + runes := []rune(s) + var b strings.Builder + b.Grow(len(s)) + for i, r := range runes { + switch { + case r == '°': + // Prose from IMGW already writes "30°C"; appending the unit again + // would give "30CC". Only a bare degree sign gains the letter. + if i+1 < len(runes) && (runes[i+1] == 'C' || runes[i+1] == 'F') { + continue + } + b.WriteString(degree) + case r < 128: + b.WriteRune(r) + default: + if sub, ok := asciiMap[r]; ok { + b.WriteString(sub) + } else { + // Anything unmapped -- a place name in another script, a weather + // code description we have not transliterated -- becomes '?' + // rather than silently vanishing and misaligning the row. + b.WriteString("?") + } + } + } + return b.String() +} |
