diff --git a/src/StructUtils.jl b/src/StructUtils.jl index c1cc2de..f866fa2 100644 --- a/src/StructUtils.jl +++ b/src/StructUtils.jl @@ -1290,8 +1290,9 @@ struct FixedArrayClosure{A,S} end function (f::FixedArrayClosure{A,S})(_, v) where {A,S} - val, st = make(f.style, eltype(f.arr), v) i = f.idx[] + i <= length(f.arr) || throw(DimensionMismatch("too many elements for fixed-size array")) + val, st = make(f.style, eltype(f.arr), v) @inbounds f.arr[i] = val f.idx[] = i + 1 return st @@ -1312,7 +1313,9 @@ function makearray(style, ::Type{T}, source) where {T} buf = reshape(data, dims) st = applyeach(style, MultiDimClosure(style, buf, ones(Int, N), Ref(N)), source) else - st = applyeach(style, FixedArrayClosure(data, style, Ref(1)), source) + idx = Ref(1) + st = applyeach(style, FixedArrayClosure(data, style, idx), source) + idx[] == L + 1 || throw(DimensionMismatch("too few elements for fixed-size array")) end return arrayfromdata(T, data, dims), st else diff --git a/test/runtests.jl b/test/runtests.jl index 13cda5f..d912169 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,15 @@ using Test, Dates, UUIDs, StructUtils struct TestStyle <: StructUtils.StructStyle end +struct FixedVectorStyle <: StructUtils.StructStyle + seen::Vector{String} +end +StructUtils.lowerkey(::FixedVectorStyle, k) = string(k) +StructUtils.defaultstate(::FixedVectorStyle) = :fixed_vector +function StructUtils.lift(st::FixedVectorStyle, ::Type{Int}, x::String) + push!(st.seen, x) + return parse(Int, x), :lifted +end struct StrictUnknownFieldStyle <: StructUtils.StructStyle end struct UnknownFieldTestError <: Exception target::Any @@ -654,6 +663,34 @@ end @test StructUtils.make(Vector{SVector{2,Int}}, [[1, 2], [3, 4]]) == [SVector{2,Int}((1, 2)), SVector{2,Int}((3, 4))] end +@testset "fixed-size vector input length" begin + for V in (SVector, MVector), E in (Int, String) + values = E === Int ? [1, 2] : ["a", "b"] + T = V{2,E} + @test StructUtils.make(T, values) == T(values) + @test_throws DimensionMismatch StructUtils.make(T, values[1:1]) + @test_throws DimensionMismatch StructUtils.make(T, vcat(values, values[1:1])) + @test StructUtils.make(V{0,E}, E[]) == V{0,E}() + @test_throws DimensionMismatch StructUtils.make(V{0,E}, values) + end + for n in (1, 2, 3) + visits = Ref(0) + source = (x for x in Iterators.filter(x -> (visits[] += 1; true), 1:n)) + @test Base.IteratorSize(typeof(source)) isa Base.SizeUnknown + if n == 2 + @test StructUtils.make(SVector{2,Int}, source) == SVector(1, 2) + else + @test_throws DimensionMismatch StructUtils.make(SVector{2,Int}, source) + end + @test visits[] == n + end + style = FixedVectorStyle(String[]) + result, state = StructUtils.make(style, SVector{2,Int}, ["1", "2"]) + @test result == SVector(1, 2) + @test state === :fixed_vector + @test style.seen == ["1", "2"] +end + @testset "absent fields take the null their type admits" begin NT = NamedTuple{(:a, :b),Tuple{Union{Missing,Int},Union{Missing,String}}} @test isequal(StructUtils.make(NT, Dict("a" => 1)), (a=1, b=missing))