summaryrefslogtreecommitdiffstats
path: root/internal/bot
diff options
context:
space:
mode:
Diffstat (limited to 'internal/bot')
-rw-r--r--internal/bot/bot.go8
-rw-r--r--internal/bot/format.go33
-rw-r--r--internal/bot/format_test.go29
3 files changed, 45 insertions, 25 deletions
diff --git a/internal/bot/bot.go b/internal/bot/bot.go
index 4abf02c..b361841 100644
--- a/internal/bot/bot.go
+++ b/internal/bot/bot.go
@@ -13,15 +13,13 @@ import (
cepis "cep.is/go"
- "cep.is/telegrambot/internal/lookup"
"cep.is/telegrambot/internal/telegram"
)
// Lookups is the subset of the cep.is client this bot needs. Depending on the
// interface rather than the concrete client keeps the seam narrow and visible.
//
-// A failure may be a [*lookup.NotFoundError], which still matches
-// cepis.ErrCEPNotFound and additionally names the municipality the CEP would
+// A failure may be a [*cepis.Error] carrying the municipality the CEP would
// belong to; nothing here has to know that, because [ErrorMessage] reads it.
type Lookups interface {
Lookup(ctx context.Context, cep string) (*cepis.Address, error)
@@ -273,8 +271,8 @@ func inlineErrorTitle(err error) string {
// Titles and descriptions are plain text for Telegram, so nothing here is
// escaped: HTML applies to the message body alone.
func inlineErrorDescription(err error) string {
- if faixa := lookup.FaixaOf(err); faixa != nil {
- return "Cairia em " + faixa.Place() + " — toque para ver"
+ if faixa := faixaOf(err); faixa != nil {
+ return "Cairia em " + place(faixa) + " — toque para ver"
}
return "Toque para enviar o motivo"
}
diff --git a/internal/bot/format.go b/internal/bot/format.go
index b94dee4..86a0ac9 100644
--- a/internal/bot/format.go
+++ b/internal/bot/format.go
@@ -9,8 +9,6 @@ import (
"time"
cepis "cep.is/go"
-
- "cep.is/telegrambot/internal/lookup"
)
// ParseMode is the formatting Telegram applies to every message this bot sends.
@@ -197,14 +195,14 @@ const (
// CEPs are spread over nearly five thousand numbers — so its ends are the
// extremes of what exists, not a list of what is valid, and the reply says so.
func notFoundMessage(err error) string {
- faixa := lookup.FaixaOf(err)
+ faixa := faixaOf(err)
if faixa == nil || faixa.Localidade == "" {
return notFoundHead + "\n" + notFoundAdvice
}
var b strings.Builder
b.WriteString(notFoundHead + "\n")
- b.WriteString("Ele cairia em <b>" + esc(faixa.Place()) + "</b>")
+ b.WriteString("Ele cairia em <b>" + esc(place(faixa)) + "</b>")
count := faixaCount(faixa)
span := faixaSpan(faixa)
@@ -225,10 +223,33 @@ func notFoundMessage(err error) string {
return b.String()
}
+// faixaOf returns the municipality the API named on a 404, or nil when it named
+// none. Any error is safe to pass, including nil: only a *cepis.Error carrying
+// a faixa yields one.
+func faixaOf(err error) *cepis.Faixa {
+ var cerr *cepis.Error
+ if errors.As(err, &cerr) {
+ return cerr.Faixa
+ }
+ return nil
+}
+
+// place is the municipality as it is written to a user: "Areal/RJ", or just
+// "Areal" when the API sent no UF.
+func place(faixa *cepis.Faixa) string {
+ if faixa == nil {
+ return ""
+ }
+ if faixa.UF == "" {
+ return faixa.Localidade
+ }
+ return faixa.Localidade + "/" + faixa.UF
+}
+
// faixaCount renders how many CEPs the municipality has, or "" when the API did
// not say. Without it the two ends of the range read as a count of one big
// block, which is the misreading the number exists to prevent.
-func faixaCount(faixa *lookup.Faixa) string {
+func faixaCount(faixa *cepis.Faixa) string {
switch {
case faixa.CEPs <= 0:
return ""
@@ -241,7 +262,7 @@ func faixaCount(faixa *lookup.Faixa) string {
// faixaSpan renders the ends of the range, or "" when they are missing or are
// the same number — "de X a X" is not a range, and printing a single CEP there
// would hand the user a guess to try.
-func faixaSpan(faixa *lookup.Faixa) string {
+func faixaSpan(faixa *cepis.Faixa) string {
first, last := maskCEP(faixa.CEPIni), maskCEP(faixa.CEPFim)
if first == "" || last == "" || first == last {
return ""
diff --git a/internal/bot/format_test.go b/internal/bot/format_test.go
index 513db36..8e10b3e 100644
--- a/internal/bot/format_test.go
+++ b/internal/bot/format_test.go
@@ -7,8 +7,6 @@ import (
"testing"
cepis "cep.is/go"
-
- "cep.is/telegrambot/internal/lookup"
)
func decodeAddress(t *testing.T, body string) *cepis.Address {
@@ -118,11 +116,14 @@ func TestErrorMessagesAreDistinct(t *testing.T) {
}
}
-func notFound(faixa *lookup.Faixa) error {
- return &lookup.NotFoundError{Faixa: faixa, Err: cepis.ErrCEPNotFound}
+// notFound builds the error the client library returns for a CEP absent from
+// the DNE, with the municipality the API named on the 404 — or without one,
+// for a CEP in a band no municipality owns.
+func notFound(faixa *cepis.Faixa) error {
+ return &cepis.Error{Kind: cepis.ErrCEPNotFound, Faixa: faixa}
}
-var areal = &lookup.Faixa{
+var areal = &cepis.Faixa{
Localidade: "Areal", UF: "RJ", Estado: "Rio de Janeiro", Regiao: "Sudeste",
IBGE: "3300225", CEPIni: "25845001", CEPFim: "25849899", CEPs: 118,
}
@@ -168,7 +169,7 @@ func TestNotFoundWithoutFaixaIsUnchanged(t *testing.T) {
for name, err := range map[string]error{
"plain sentinel": cepis.ErrCEPNotFound,
"no faixa": notFound(nil),
- "empty faixa": notFound(&lookup.Faixa{}),
+ "empty faixa": notFound(&cepis.Faixa{}),
} {
if got := ErrorMessage(err); got != want {
t.Fatalf("%s:\n got %q\nwant %q", name, got, want)
@@ -177,7 +178,7 @@ func TestNotFoundWithoutFaixaIsUnchanged(t *testing.T) {
}
func TestNotFoundEscapesTheMunicipality(t *testing.T) {
- text := ErrorMessage(notFound(&lookup.Faixa{
+ text := ErrorMessage(notFound(&cepis.Faixa{
Localidade: "Santa <b>Rita</b> & Cia", UF: "M&G",
CEPIni: "25845001", CEPFim: "25849899", CEPs: 2,
}))
@@ -194,32 +195,32 @@ func TestNotFoundEscapesTheMunicipality(t *testing.T) {
// take the sentence down with it.
func TestNotFoundDegradesFieldByField(t *testing.T) {
cases := map[string]struct {
- faixa *lookup.Faixa
+ faixa *cepis.Faixa
present []string
absent []string
}{
"no count": {
- faixa: &lookup.Faixa{Localidade: "Areal", UF: "RJ", CEPIni: "25845001", CEPFim: "25849899"},
+ faixa: &cepis.Faixa{Localidade: "Areal", UF: "RJ", CEPIni: "25845001", CEPFim: "25849899"},
present: []string{"Areal/RJ", "25845-001", "25849-899", "esparsa"},
absent: []string{"CEPs,"},
},
"no range": {
- faixa: &lookup.Faixa{Localidade: "Areal", UF: "RJ", CEPs: 118},
+ faixa: &cepis.Faixa{Localidade: "Areal", UF: "RJ", CEPs: 118},
present: []string{"Areal/RJ", "118 CEPs"},
absent: []string{"<code>", "esparsa"},
},
"only the name": {
- faixa: &lookup.Faixa{Localidade: "Areal"},
+ faixa: &cepis.Faixa{Localidade: "Areal"},
present: []string{"cairia em <b>Areal</b>."},
absent: []string{"<code>", "esparsa", "Areal/"},
},
"single CEP": {
- faixa: &lookup.Faixa{Localidade: "Vila", UF: "SP", CEPIni: "12345678", CEPFim: "12345678", CEPs: 1},
+ faixa: &cepis.Faixa{Localidade: "Vila", UF: "SP", CEPIni: "12345678", CEPFim: "12345678", CEPs: 1},
present: []string{"1 CEP."},
absent: []string{"12345-678"},
},
"unusable range": {
- faixa: &lookup.Faixa{Localidade: "Areal", UF: "RJ", CEPIni: "abc", CEPFim: "2584", CEPs: 118},
+ faixa: &cepis.Faixa{Localidade: "Areal", UF: "RJ", CEPIni: "abc", CEPFim: "2584", CEPs: 118},
present: []string{"118 CEPs."},
absent: []string{"abc", "2584"},
},
@@ -251,7 +252,7 @@ func TestInlineErrorDescriptionNamesTheMunicipality(t *testing.T) {
}
// Titles and descriptions are plain text: escaping them would show the
// entities to the user.
- got := inlineErrorDescription(notFound(&lookup.Faixa{Localidade: "Santa & Cia", UF: "SP"}))
+ got := inlineErrorDescription(notFound(&cepis.Faixa{Localidade: "Santa & Cia", UF: "SP"}))
if !strings.Contains(got, "Santa & Cia/SP") {
t.Fatalf("description = %q", got)
}