summaryrefslogtreecommitdiffstats
path: root/src/Cepis/FaixaReader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Cepis/FaixaReader.cs')
-rw-r--r--src/Cepis/FaixaReader.cs98
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,
+ };
+ }
+}