diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 12:42:38 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 13:10:34 -0300 |
| commit | a2b10fd494fb389e556e40134031eb86e56e11e7 (patch) | |
| tree | 08b6df8a06f2c7c15488b90d57a6d8286cd57a22 /src/Cepis/Address.cs | |
| download | csharp-a2b10fd494fb389e556e40134031eb86e56e11e7.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'src/Cepis/Address.cs')
| -rw-r--r-- | src/Cepis/Address.cs | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/Cepis/Address.cs b/src/Cepis/Address.cs new file mode 100644 index 0000000..edc7776 --- /dev/null +++ b/src/Cepis/Address.cs @@ -0,0 +1,205 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Cepis; + +/// <summary> +/// A CEP and everything the database knows about it. +/// </summary> +/// <remarks> +/// <para> +/// The property names are the API's own, in Portuguese, with only the casing +/// adapted to C#. Everything else in this library is English; the data fields +/// are the one deliberate exception, so that what you read in the JSON at +/// <c>https://cep.is/01001000</c> is what you access on the object. The +/// mapping from wire name to property is: +/// </para> +/// <list type="table"> +/// <listheader><term>wire</term><description>property</description></listheader> +/// <item><term><c>cep</c></term><description><see cref="Cep"/></description></item> +/// <item><term><c>logradouro</c></term><description><see cref="Logradouro"/></description></item> +/// <item><term><c>complemento</c></term><description><see cref="Complemento"/></description></item> +/// <item><term><c>unidade</c></term><description><see cref="Unidade"/></description></item> +/// <item><term><c>bairro</c></term><description><see cref="Bairro"/></description></item> +/// <item><term><c>localidade</c></term><description><see cref="Localidade"/></description></item> +/// <item><term><c>uf</c></term><description><see cref="UF"/></description></item> +/// <item><term><c>estado</c></term><description><see cref="Estado"/></description></item> +/// <item><term><c>regiao</c></term><description><see cref="Regiao"/></description></item> +/// <item><term><c>ibge</c></term><description><see cref="Ibge"/></description></item> +/// <item><term><c>gia</c></term><description><see cref="Gia"/></description></item> +/// <item><term><c>ddd</c></term><description><see cref="Ddd"/></description></item> +/// <item><term><c>siafi</c></term><description><see cref="Siafi"/></description></item> +/// <item><term><c>geo</c></term><description><see cref="Geo"/></description></item> +/// </list> +/// <para> +/// Fields the database does not have for that CEP come back as empty strings, +/// never <see langword="null"/> — that is what the API returns. +/// </para> +/// <para> +/// <see cref="Geo"/> is the exception: it is <see langword="null"/> when there +/// is no coordinate. There the difference is real, because the API <b>omits</b> +/// the whole field in that case (about 43% of CEPs) rather than sending null. +/// It is also always <see langword="null"/> for an address that came from +/// <see cref="CepisClient.LookupManyAsync(IEnumerable{string}, CancellationToken)"/>, +/// which carries no coordinate at all. +/// </para> +/// <para> +/// <see cref="Extra"/> holds any field the API has grown that this version of +/// the library does not model, so an outdated client loses nothing. It takes no +/// part in equality, hashing or <see cref="object.ToString"/>: two addresses +/// describing the same place are equal even if one of them was built by hand. +/// </para> +/// </remarks> +public sealed record Address +{ + /// <summary>The eight digits, always unmasked. Wire name <c>cep</c>.</summary> + [JsonPropertyName("cep")] + public string Cep { get; init; } = ""; + + /// <summary>The street. Wire name <c>logradouro</c>.</summary> + [JsonPropertyName("logradouro")] + public string Logradouro { get; init; } = ""; + + /// <summary> + /// The qualifier on the street, such as a range of numbers. Wire name + /// <c>complemento</c>. + /// </summary> + [JsonPropertyName("complemento")] + public string Complemento { get; init; } = ""; + + /// <summary> + /// The named unit, for a CEP that belongs to one. Wire name <c>unidade</c>. + /// </summary> + [JsonPropertyName("unidade")] + public string Unidade { get; init; } = ""; + + /// <summary>The neighbourhood. Wire name <c>bairro</c>.</summary> + [JsonPropertyName("bairro")] + public string Bairro { get; init; } = ""; + + /// <summary>The municipality. Wire name <c>localidade</c>.</summary> + [JsonPropertyName("localidade")] + public string Localidade { get; init; } = ""; + + /// <summary>The two-letter state code. Wire name <c>uf</c>.</summary> + [JsonPropertyName("uf")] + public string UF { get; init; } = ""; + + /// <summary>The state's name. Wire name <c>estado</c>.</summary> + [JsonPropertyName("estado")] + public string Estado { get; init; } = ""; + + /// <summary>The region of the country. Wire name <c>regiao</c>.</summary> + [JsonPropertyName("regiao")] + public string Regiao { get; init; } = ""; + + /// <summary>The IBGE municipality code. Wire name <c>ibge</c>.</summary> + [JsonPropertyName("ibge")] + public string Ibge { get; init; } = ""; + + /// <summary>The GIA code, São Paulo only. Wire name <c>gia</c>.</summary> + [JsonPropertyName("gia")] + public string Gia { get; init; } = ""; + + /// <summary>The telephone area code. Wire name <c>ddd</c>.</summary> + [JsonPropertyName("ddd")] + public string Ddd { get; init; } = ""; + + /// <summary>The SIAFI municipality code. Wire name <c>siafi</c>.</summary> + [JsonPropertyName("siafi")] + public string Siafi { get; init; } = ""; + + /// <summary> + /// The coordinate, or <see langword="null"/> when there is none. Wire name + /// <c>geo</c>. + /// </summary> + /// <remarks> + /// A malformed <c>geo</c> block yields <see langword="null"/> rather than + /// failing the whole response: the rest of the address is still useful. + /// </remarks> + [JsonPropertyName("geo")] + [JsonConverter(typeof(GeoConverter))] + public Geo? Geo { get; init; } + + /// <summary> + /// Fields the API returned that this version of the library does not model. + /// </summary> + /// <remarks> + /// A new field in the API therefore cannot break this client, and cannot be + /// silently dropped either. Empty for an address built by hand. + /// </remarks> + [JsonExtensionData] + public Dictionary<string, JsonElement>? Extra { get; init; } + + /// <summary>The CEP with its mask, <c>01001-000</c>.</summary> + /// <remarks> + /// The API always returns it unmasked; formatting is presentation, and it + /// lives here so it does not become a repeated interpolation in every + /// project that uses this library. A CEP that is not eight characters long + /// is returned unchanged. + /// </remarks> + [JsonIgnore] + public string FormattedCep => Cep.Length == 8 ? $"{Cep[..5]}-{Cep[5..]}" : Cep; + + /// <summary>Compare every field but <see cref="Extra"/>.</summary> + /// <param name="other">the address to compare with</param> + /// <returns><see langword="true"/> when both describe the same place</returns> + public bool Equals(Address? other) => + other is not null + && Cep == other.Cep + && Logradouro == other.Logradouro + && Complemento == other.Complemento + && Unidade == other.Unidade + && Bairro == other.Bairro + && Localidade == other.Localidade + && UF == other.UF + && Estado == other.Estado + && Regiao == other.Regiao + && Ibge == other.Ibge + && Gia == other.Gia + && Ddd == other.Ddd + && Siafi == other.Siafi + && Geo == other.Geo; + + /// <summary>Hash every field but <see cref="Extra"/>.</summary> + /// <returns>a hash consistent with <see cref="Equals(Address)"/></returns> + public override int GetHashCode() + { + var hash = default(HashCode); + hash.Add(Cep); + hash.Add(Logradouro); + hash.Add(Complemento); + hash.Add(Unidade); + hash.Add(Bairro); + hash.Add(Localidade); + hash.Add(UF); + hash.Add(Estado); + hash.Add(Regiao); + hash.Add(Ibge); + hash.Add(Gia); + hash.Add(Ddd); + hash.Add(Siafi); + hash.Add(Geo); + return hash.ToHashCode(); + } + + private bool PrintMembers(StringBuilder builder) + { + builder.Append("Cep = ").Append(Cep) + .Append(", Logradouro = ").Append(Logradouro) + .Append(", Complemento = ").Append(Complemento) + .Append(", Unidade = ").Append(Unidade) + .Append(", Bairro = ").Append(Bairro) + .Append(", Localidade = ").Append(Localidade) + .Append(", UF = ").Append(UF) + .Append(", Estado = ").Append(Estado) + .Append(", Regiao = ").Append(Regiao) + .Append(", Ibge = ").Append(Ibge) + .Append(", Gia = ").Append(Gia) + .Append(", Ddd = ").Append(Ddd) + .Append(", Siafi = ").Append(Siafi) + .Append(", Geo = ").Append(Geo?.ToString() ?? "null"); + return true; + } +} |