// Package cepis is a Go client for the public CEP API at https://cep.is. // Official Correios data (DNE), refreshed every two weeks. // // For a script, the package-level functions are enough — they share one // default [Client] with the default configuration: // // addr, err := cepis.Lookup(ctx, "01001-000") // if err != nil { // return err // } // fmt.Println(addr.Logradouro, addr.Localidade, addr.UF) // // In an application, build a client once and reuse it: // // client, err := cepis.New( // cepis.WithTimeout(5*time.Second), // cepis.WithTotalTimeout(15*time.Second), // ) // // Pointing at a local instance (the API repo brings one up with `just up`): // // client, err := cepis.New(cepis.WithBaseURL("http://localhost:8080")) // // Errors are split by the decision the caller has to make — whether retrying // helps or not — and all of them match [Err]: // // switch { // case errors.Is(err, cepis.ErrCEPNotFound): // // the CEP does not exist: ask the user for another // case errors.Is(err, cepis.Err): // // network, server, rate limit: retry later // } // // The API surface is English, but the data fields keep the API's own // Portuguese names — Logradouro, Bairro, Localidade. What you read in the JSON // is what you access on the struct. package cepis import ( "context" "strings" ) var defaultClient = mustDefault() func mustDefault() *Client { client, err := New() if err != nil { panic("cepis: default client is not constructible: " + err.Error()) } return client } // Default returns the client behind the package-level functions. It uses the // default configuration and is safe to share across goroutines. It cannot be // reconfigured on purpose: it applies process-wide, third-party libraries // included. Anyone needing different settings should build their own with // [New]. func Default() *Client { return defaultClient } // Lookup resolves a CEP using the default client. See [Client.Lookup]. func Lookup(ctx context.Context, cep string) (*Address, error) { return defaultClient.Lookup(ctx, cep) } // LookupMany resolves many CEPs in one batch using the default client. See // [Client.LookupMany], especially the note that batches carry no coordinate. func LookupMany(ctx context.Context, ceps []string) ([]*Address, error) { return defaultClient.LookupMany(ctx, ceps) } // CheckHealth queries the API's health using the default client. See // [Client.Health]. It is not called Health because that name belongs to the // type it returns. func CheckHealth(ctx context.Context) (*Health, error) { return defaultClient.Health(ctx) } // NormalizeCEP returns the CEP's 8 digits, accepting the mask: "01001-000", // "01001000" and " 01001-000 " all become "01001000". // // It fails with [ErrInvalidCEP] unless exactly 8 digits remain. Every lookup // runs it before making a request: a 7-digit CEP is the caller's bug, and // spending a round trip for the server to say so wastes both sides' time. func NormalizeCEP(value string) (string, error) { cleaned := strings.Map(func(r rune) rune { switch r { case '-', '.', ' ', '\t', '\n', '\r': return -1 } return r }, value) if len(cleaned) != 8 { return "", newError(ErrInvalidCEP, "CEP must be 8 digits, got %q", value) } for _, r := range cleaned { if r < '0' || r > '9' { return "", newError(ErrInvalidCEP, "CEP must be 8 digits, got %q", value) } } return cleaned, nil }