diff options
Diffstat (limited to 'src/Cepis/FaixaReader.cs')
| -rw-r--r-- | src/Cepis/FaixaReader.cs | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/Cepis/FaixaReader.cs b/src/Cepis/FaixaReader.cs new file mode 100644 index 0000000..da3c6a2 --- /dev/null +++ b/src/Cepis/FaixaReader.cs @@ -0,0 +1,98 @@ +using System.Globalization; +using System.Text.Json; + +namespace Cepis; + +/// <summary> +/// Reads the <c>faixa</c> block of a 404 body, yielding <see langword="null"/> +/// for anything it cannot make sense of. +/// </summary> +/// <remarks> +/// <para> +/// A reader rather than a <see cref="System.Text.Json.Serialization.JsonConverter{T}"/> +/// because <c>faixa</c> is not a field of any model this library deserializes: +/// it hangs off an error body that is otherwise only read for its message. +/// </para> +/// <para> +/// Nothing in here throws. The block is a courtesy on top of a 404 — a missing, +/// misshapen or half-empty one must leave the caller with the same clean +/// <see cref="CepNotFoundException"/> it would have got before the API grew the +/// field, never with a parse failure on top of a not-found. +/// </para> +/// </remarks> +internal static class FaixaReader +{ + /// <summary>Turn the <c>faixa</c> element into a range, or null.</summary> + /// <param name="element">the block, as it came off the wire</param> + /// <returns>the range, or null when there is nothing usable in it</returns> + internal static Faixa? Read(JsonElement element) + { + if (element.ValueKind != JsonValueKind.Object) + { + return null; + } + + var localidade = ReadString(element, "localidade"); + if (string.IsNullOrWhiteSpace(localidade)) + { + return null; + } + + return new Faixa + { + Localidade = localidade, + UF = ReadString(element, "uf"), + Estado = ReadString(element, "estado"), + Regiao = ReadString(element, "regiao"), + Ibge = ReadString(element, "ibge"), + CepIni = ReadString(element, "cep_ini"), + CepFim = ReadString(element, "cep_fim"), + Ceps = ReadInt(element, "ceps"), + }; + } + + /// <summary> + /// The property as text, or the empty string when it is absent or of a + /// shape that is not text. + /// </summary> + /// <remarks> + /// A number is accepted and taken verbatim, the way + /// <see cref="LenientStringConverter"/> does it for the address fields. + /// </remarks> + private static string ReadString(JsonElement owner, string name) + { + if (!owner.TryGetProperty(name, out var element)) + { + return ""; + } + + return element.ValueKind switch + { + JsonValueKind.String => element.GetString() ?? "", + JsonValueKind.Number => element.GetRawText(), + _ => "", + }; + } + + /// <summary>The property as a count, or zero when it cannot be read.</summary> + private static int ReadInt(JsonElement owner, string name) + { + if (!owner.TryGetProperty(name, out var element)) + { + return 0; + } + + return element.ValueKind switch + { + JsonValueKind.Number => element.TryGetInt32(out var number) ? number : 0, + JsonValueKind.String => int.TryParse( + element.GetString(), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out var parsed) + ? parsed + : 0, + _ => 0, + }; + } +} |