Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/StructTypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,8 @@ and respecting various serialization/deserialization names, keyword args, etc.

Most typical use-case is construct a custom type `T` from an `obj::AbstractDict`, but
`constructfrom` is fully generic, so the inverse is also supported (turning any custom
struct into an `AbstractDict`). For example, an external service may be providing JSON
struct into an `AbstractDict`). A `CustomStruct` source is converted through its
`lower` method. For example, an external service may be providing JSON
data with an evolving schema; as opposed to trying a strict "typed parsing" like
`JSON3.read(json, T)`, it may be preferrable to setup a local custom struct with just
the desired properties and call `StructTypes.constructfrom(T, JSON3.read(json))`. This
Expand Down Expand Up @@ -1007,6 +1008,9 @@ constructfrom(::DictType, ::Type{T}, obj) where {T <: AbstractDict} =
@inline constructfrom(::DictType, ::Type{T}, ::Type{K}, ::Type{V}, obj::S) where {T, K, V, S} =
constructfrom(DictType(), T, K, V, StructType(S), obj)

constructfrom(::DictType, ::Type{T}, ::Type{K}, ::Type{V}, ::CustomStruct, obj) where {T, K, V} =
constructfrom(DictType(), T, K, V, lower(obj))

function constructfrom(::DictType, ::Type{T}, ::Type{K}, ::Type{V}, ::DictType, obj) where {T, K, V}
d = Dict{K, V}()
for (k, v) in keyvaluepairs(obj)
Expand Down Expand Up @@ -1068,6 +1072,9 @@ function constructfrom!(::Mutable, x::T, ::Union{Struct, Mutable}, obj) where {T
return x
end

constructfrom!(::Mutable, x::T, ::CustomStruct, obj) where {T} =
constructfrom!(x, lower(obj))

constructfrom(::Struct, ::Type{T}, obj::S) where {T, S} =
constructfrom(Struct(), T, StructType(S), obj)

Expand Down Expand Up @@ -1102,6 +1109,9 @@ end
constructfrom(::Struct, ::Type{T}, ::Union{Struct, Mutable}, obj) where {T} =
construct(StructClosure(obj), T)

constructfrom(::Struct, ::Type{T}, ::CustomStruct, obj) where {T} =
constructfrom(T, lower(obj))

struct TupleClosure{T}
obj::T
end
Expand Down
39 changes: 39 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -877,3 +877,42 @@ end
@testset "struct type on complex" begin
@test StructTypes.StructType(ComplexF64) == StructTypes.Struct()
end

struct CustomColumn{T, S}
values::Vector{T}
end
StructTypes.StructType(::Type{<:CustomColumn}) = StructTypes.CustomStruct()
StructTypes.lowertype(::Type{<:CustomColumn}) = Dict{String, Any}
StructTypes.lower(c::CustomColumn{T, S}) where {T, S} = Dict{String, Any}(
"type" => string(T), "storage" => string(S), "values" => c.values)

mutable struct MutableColumn
type::String
values::Vector{Int}
MutableColumn() = new()
end
StructTypes.StructType(::Type{MutableColumn}) = StructTypes.Mutable()

struct ImmutableColumn
type::String
values::Vector{Int}
end
StructTypes.StructType(::Type{ImmutableColumn}) = StructTypes.Struct()

struct LowersToNamedTuple
x::NamedTuple
end
StructTypes.StructType(::Type{LowersToNamedTuple}) = StructTypes.CustomStruct()
StructTypes.lower(x::LowersToNamedTuple) = x.x

@testset "constructfrom with CustomStruct sources" begin
c = CustomColumn{Int, Nothing}([1, 2])
@test StructTypes.constructfrom(Dict{String, Any}, c) ==
Dict("type" => string(Int), "storage" => "Nothing", "values" => [1, 2])
@test StructTypes.constructfrom(Dict{Symbol, Any}, c)[:storage] == "Nothing"
w = LowersToNamedTuple((type="Int64", values=[1, 2]))
m = StructTypes.constructfrom(MutableColumn, w)
@test (m.type, m.values) == ("Int64", [1, 2])
s = StructTypes.constructfrom(ImmutableColumn, w)
@test (s.type, s.values) == ("Int64", [1, 2])
end
Loading