Support parenthesized NWS discussion headings
This commit is contained in:
@@ -426,6 +426,11 @@ func parseForecastDiscussionSectionHeading(line string) (forecastDiscussionSecti
|
||||
if strings.HasSuffix(line, "/...") {
|
||||
return parseForecastDiscussionSlashQualifiedHeading(line)
|
||||
}
|
||||
if strings.HasSuffix(line, "...") {
|
||||
if heading, ok := parseForecastDiscussionParenthesizedTerminalHeading(line); ok {
|
||||
return heading, true
|
||||
}
|
||||
}
|
||||
return parseForecastDiscussionEllipsisHeading(line)
|
||||
}
|
||||
|
||||
@@ -453,6 +458,38 @@ func parseForecastDiscussionSlashQualifiedHeading(line string) (forecastDiscussi
|
||||
return forecastDiscussionSectionHeading{section: section, qualifier: qualifier}, true
|
||||
}
|
||||
|
||||
func parseForecastDiscussionParenthesizedTerminalHeading(line string) (forecastDiscussionSectionHeading, bool) {
|
||||
if len(line) < 4 || line[0] != '.' || !strings.HasSuffix(line, "...") {
|
||||
return forecastDiscussionSectionHeading{}, false
|
||||
}
|
||||
|
||||
content := strings.TrimRight(line[1:len(line)-3], " \t")
|
||||
if !strings.HasSuffix(content, ")") {
|
||||
return forecastDiscussionSectionHeading{}, false
|
||||
}
|
||||
|
||||
separator := -1
|
||||
for i := 1; i < len(content); i++ {
|
||||
if content[i] == '(' && isForecastDiscussionHorizontalWhitespace(content[i-1]) {
|
||||
separator = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if separator < 0 {
|
||||
return forecastDiscussionSectionHeading{}, false
|
||||
}
|
||||
|
||||
section, ok := normalizeForecastDiscussionSectionIdentity(content[:separator])
|
||||
if !ok {
|
||||
return forecastDiscussionSectionHeading{}, false
|
||||
}
|
||||
qualifier := content[separator:]
|
||||
if len(qualifier) <= 2 || strings.TrimSpace(qualifier[1:len(qualifier)-1]) == "" {
|
||||
return forecastDiscussionSectionHeading{}, false
|
||||
}
|
||||
return forecastDiscussionSectionHeading{section: section, qualifier: qualifier}, true
|
||||
}
|
||||
|
||||
func parseForecastDiscussionEllipsisHeading(line string) (forecastDiscussionSectionHeading, bool) {
|
||||
content := line[1:]
|
||||
delimiter := strings.Index(content, "...")
|
||||
|
||||
@@ -129,6 +129,34 @@ func TestParseForecastDiscussionSectionHeading(t *testing.T) {
|
||||
wantQualifier: "(Tonight)",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal discussion",
|
||||
line: ".DISCUSSION (Today through Thursday)...",
|
||||
wantSection: "DISCUSSION",
|
||||
wantQualifier: "(Today through Thursday)",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal mapped section",
|
||||
line: ".SHORT TERM (Tonight)...",
|
||||
wantSection: "SHORT TERM",
|
||||
wantQualifier: "(Tonight)",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal normalized identity whitespace",
|
||||
line: " .SHORT\tTERM (Tonight)... ",
|
||||
wantSection: "SHORT TERM",
|
||||
wantQualifier: "(Tonight)",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal qualifier punctuation and nesting",
|
||||
line: ".DISCUSSION (Today (and Thursday), 70% chance!)...",
|
||||
wantSection: "DISCUSSION",
|
||||
wantQualifier: "(Today (and Thursday), 70% chance!)",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "lowercase prose",
|
||||
line: ".This is ordinary prose...",
|
||||
@@ -194,6 +222,41 @@ func TestParseForecastDiscussionSectionHeading(t *testing.T) {
|
||||
line: ".Short Term... (Tonight)",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal missing separator whitespace",
|
||||
line: ".DISCUSSION(Today)...",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal empty qualifier",
|
||||
line: ".DISCUSSION ( )...",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal missing final parenthesis",
|
||||
line: ".DISCUSSION (Today...",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal misplaced parentheses",
|
||||
line: ".DISCUSSION Today)...",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal trailing text",
|
||||
line: ".DISCUSSION (Today)... extra",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal unsupported identity punctuation",
|
||||
line: ".DISCUSSION: (Today)...",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "parenthesized terminal lowercase identity",
|
||||
line: ".discussion (Today)...",
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -310,6 +373,28 @@ func TestParseForecastDiscussionSectionBlocksUsesGenericHeadingsAsBoundaries(t *
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionSectionBlocksUsesParenthesizedTerminalHeadingAsBoundary(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".SHORT TERM... (Tonight)",
|
||||
"Short-term prose.",
|
||||
".DISCUSSION (Today through Thursday)...",
|
||||
"Discussion prose.",
|
||||
})
|
||||
want := []forecastDiscussionSectionBlock{
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "SHORT TERM", qualifier: "(Tonight)"},
|
||||
body: []string{"Short-term prose."},
|
||||
},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "DISCUSSION", qualifier: "(Today through Thursday)"},
|
||||
body: []string{"Discussion prose."},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("blocks = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionSectionBlocksKeepsMalformedHeadingLikeLines(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".SHORT TERM... (Tonight)",
|
||||
|
||||
Reference in New Issue
Block a user