From b108d05b6baca59636898ad5f2c9cce3df29a465 Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Thu, 22 Apr 2021 23:52:41 -0600 Subject: [PATCH] Fix when omitempties(T) = true is defined in StructTypes.foreachfield (#50) I noticed in some serialized JSON from JSON3.jl recently that some fields were still getting serialized when I had defined `StructTypes.omitempties(::Type{MyType}) = true`, which is a shorthand we introduced when you want to omit empty values for any field in a type. Unfortunately, we failed to update `StructTypes.foreachfield` to respect this option and double unfortunately, the code still happened to "work", i.e. not throw an error whether `omitempties` returned a `Bool` or `Tuple`. The fix is pretty simple: if `omitempties` returns `true`, then we query the `fieldnames` of the struct. --- src/StructTypes.jl | 4 +--- test/runtests.jl | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/StructTypes.jl b/src/StructTypes.jl index e74525f..d184474 100644 --- a/src/StructTypes.jl +++ b/src/StructTypes.jl @@ -628,7 +628,7 @@ Various "configurations" are respected when applying `f` to each field: excl = excludes(T) nms = names(T) kwargs = keywordargs(T) - emp = omitempties(T) + emp = omitempties(T) === true ? fieldnames(T) : omitempties(T) Base.@nexprs 32 i -> begin k_i = fieldname(T, i) if !symbolin(excl, k_i) && isdefined(x, i) @@ -673,7 +673,6 @@ Nothing is returned and results from `f` are ignored. Similar to `Base.foreach` Various "configurations" are respected when applying `f` to each field: * If keyword arguments have been defined for a field via `StructTypes.keywordargs`, they will be passed like `f(i, name, FT, v; kw...)` * If `StructTypes.names` has been defined, `name` will be the serialization name instead of the defined julia field name - * If a field is undefined or empty and `StructTypes.omitempties` is defined, `f` won't be applied to that field * If a field has been excluded via `StructTypes.excludes`, it will be skipped """ @inline function foreachfield(f, ::Type{T}) where {T} @@ -682,7 +681,6 @@ Various "configurations" are respected when applying `f` to each field: excl = excludes(T) nms = names(T) kwargs = keywordargs(T) - emp = omitempties(T) Base.@nexprs 32 i -> begin k_i = fieldname(T, i) if !symbolin(excl, k_i) diff --git a/test/runtests.jl b/test/runtests.jl index d408ce8..0df6e90 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,6 +13,10 @@ end struct EmptyStruct end +struct OmitEmp + x::Union{Nothing, Int} +end + @testset "StructTypes" begin @test StructTypes.StructType(Union{Int, Missing}) == StructTypes.Struct() @@ -127,6 +131,18 @@ v = v"1.2.3" x = Dict("hey" => "ho") @test StructTypes.construct(Dict{String, String}, x) === x +# omitempties(T) = true respected +StructTypes.StructType(::Type{OmitEmp}) = StructTypes.Struct() +StructTypes.omitempties(::Type{OmitEmp}) = true +StructTypes.foreachfield(OmitEmp(1)) do i, nm, T, val + @test val == 1 +end +counter = 0 +StructTypes.foreachfield(OmitEmp(nothing)) do i, nm, T, val + counter += 1 +end +@test counter == 0 + end struct B