API Reference
StructUtils.DefaultStyle — TypeStructUtils.DefaultStyleDefault struct style that all StructUtils.jl interface methods are defined for by default.
StructUtils.EarlyReturn — TypeStructUtils.EarlyReturn{T}A wrapper type that can be used in function arguments to applyeach to short-circuit iteration and return a value from applyeach.
Example usage:
julia function find_needle_in_haystack(haystack, needle) ret = applyeach(haystack) do k, v k == needle && return StructUtils.EarlyReturn(v) end ret isa StructUtils.EarlyReturn && return ret.value throw(ArgumentError("needle not found in haystack") end`
StructUtils.StructStyle — TypeStructUtils.StructStyleAbstract type that all concrete struct styles must subtype. Custom struct styles allow fine-grained control over various StructUtils.jl interface methods like fieldtags, fielddefaults, lift, lower, etc.
StructUtils.addkeyval! — FunctionStructUtils.addkeyval!(d, k, v)Add a key-value pair to a dictionary-like object d. This function is called by StructUtils.make when d is dictlike. The default implementation is to call d[k] = v for AbstractDict.
StructUtils.applyeach — FunctionStructUtils.applyeach(style, f, x) -> Union{StructUtils.EarlyReturn, Nothing}A custom foreach-like function that operates specifically on (key, val) or (ind, val) pairs, and supports short-circuiting (via StructUtils.EarlyReturn). It also supports a StructStyle argument to allow for style-specific behavior for non-owned types.
For each key-value or index-value pair in x, call f(k, v). If f returns a StructUtils.EarlyReturn instance, applyeach should return the EarlyReturn immediately and stop iterating (i.e. short-circuit). Otherwise, the return value of f can be ignored and iteration continues.
Key types are generally expected to be Symbols, Strings, or Integers.
An example overload of applyeach for a generic iterable would be:
function StructUtils.applyeach(style::StructUtils.StructStyle, f, x::MyIterable)
for (i, v) in enumerate(x)
ret = f(StructUtils.lowerkey(style, i), StructUtils.lower(style, v))
# if `f` returns EarlyReturn, return immediately
ret isa StructUtils.EarlyReturn && return ret
end
return
endNote that applyeach must include the style argument when overloading.
Also note that before applying f, the key or index is passed through StructUtils.lowerkey(style, k), and the value v is passed through StructUtils.lower(style, v).
If a value is #undef or otherwise not defined, the f function should generally be called with nothing or skipped.
StructUtils.arraylike — FunctionStructUtils.arraylike(x) -> Bool StructUtils.arraylike(::StructStyle, x) -> Bool StructUtils.arraylike(::StructStyle, ::Type{T}) -> Bool
Returns true if x or type T is array-like, false otherwise. This function is called by StructUtils.make to determine if T is array-like. The default implementation returns true for <:AbstractArray, <:AbstractSet, <:Tuple, <:Base.Generator, and <:Core.SimpleVector types, and false for <:AbstractArray{T,0}.
Once initialize is called, StructUtils.make will call push! to add values to the array-like object.
StructUtils.defaultstate — MethodStructUtils.defaultstate(::StructStyle) -> AnyReturns the default state for a given struct style. This is used to initialize the state of a struct when no state is provided. The default implementation returns nothing.
StructUtils.dictlike — FunctionStructUtils.dictlike(x) -> Bool StructUtils.dictlike(::StructStyle, x) -> Bool StructUtils.dictlike(::StructStyle, ::Type{T}) -> Bool
Returns true if x or type T is dictionary-like, false otherwise. When StructUtils.make(T, source) is called, if dictlike(T) is true, an instance will be initialized, and then addkeyval!ed for each key-value pair in source.
StructUtils.fielddefault — FunctionStructUtils.fielddefaults(::StructStyle, ::Type{T}) -> NamedTuple
StructUtils.fielddefault(::StructStyle, ::Type{T}, fieldname) -> NamedTupleReturns a NamedTuple of field defaults for the struct T. Field defaults can be added manually by overloading fielddefaults, or included via convenient syntax using the StructUtils.jl macros: @tags, @noarg, @defaults, or @kwarg.
StructUtils.fielddefaults — FunctionStructUtils.fielddefaults(::StructStyle, ::Type{T}) -> NamedTuple
StructUtils.fielddefault(::StructStyle, ::Type{T}, fieldname) -> NamedTupleReturns a NamedTuple of field defaults for the struct T. Field defaults can be added manually by overloading fielddefaults, or included via convenient syntax using the StructUtils.jl macros: @tags, @noarg, @defaults, or @kwarg.
StructUtils.fieldtagkey — FunctionStructUtils.fieldtagkey(::StructStyle) -> SymbolField tags defined on struct fields can be grouped by keys that are associated with a particular struct style. This function returns the key that should be used to retrieve field tags for a given struct style. By default, this function returns nothing. An example overload might look like:
struct MySQLStyle <: StructStyle end
StructUtils.fieldtagkey(::MySQLStyle) = :mysql
@tags struct Foo
a::Int &(mysql=(name="foo_a",),)
b::String
endIn this example, when StructUtils.make is called on Foo with the MySQLStyle style, only (name="foo_a",) will be retrieved from the field tags for a because the mysql key is associated with the MySQLStyle struct style. In other words, fieldtag keys allow custom struct styles to "namespace" field tags so structs can overload specific tags in multiple ways for different namespaces, i.e. a::Int &(mysql=(name="foo_a",), json=(name="json_a",)).
StructUtils.fieldtags — FunctionStructUtils.fieldtags(::StructStyle, ::Type{T}) -> NamedTuple
StructUtils.fieldtags(::StructStyle, ::Type{T}, fieldname) -> NamedTupleReturns a NamedTuple of field tags for the struct T. Field tags can be added manually by overloading fieldtags, or included via convenient syntax using the StructUtils.jl macros: @tags, @noarg, @defaults, or @kwarg. Note this function returns the tags of all fields as a single NamedTuple.
StructUtils.initialize — FunctionStructUtils.initialize(::StructStyle, T, source) -> TIn StructUtils.make, this function is called to initialize a new instance of T, when T is dictlike, arraylike, or noarg. The source is passed from the call to make, and can be used for initialization if appropriate. The default implementation of initialize is to call T() or T(undef, 0) for <:AbstractArray types.
StructUtils.kwarg — FunctionStructUtils.kwarg(x) -> Bool StructUtils.kwarg(::StructStyle, x) -> Bool StructUtils.kwarg(::StructStyle, ::Type{T}) -> Bool
Signals that x or type T can be constructed by passing struct fields as keyword arguments to the constructor, like t = T(field1=a, field2=b, ...). Automatically overloaded when structs use the StructUtils.@kwarg macro in their struct definition. The default value is false unless explicitly overloaded.
Note that StructUtils.@kwarg is a separate implementation of Base.@kwdef, yet should be a drop-in replacement for it.
StructUtils.lift — FunctionStructUtils.lift(::Type{T}, x) -> T StructUtils.lift(::StructStyle, ::Type{T}, x) -> Tuple{T, Any}
Lifts a value x to a type T. This function is called by StructUtils.make to lift unit/atom values to the appropriate type. The default implementation is the identity function for most types, but it also includes special cases for Symbol, Char, UUID, VersionNumber, Regex, and TimeType types to be constructed from strings. Allows transforming a "domain value" that may be some primitive representation into a more complex Julia type.
The method with a StructStyle argument should return a tuple of the lifted value and any side-effect state derived from lifting the value.
StructUtils.liftkey — FunctionStructUtils.liftkey(::Type{T}, x) -> x StructUtils.liftkey(style::StructStyle, ::Type{T}, x) -> x
Allows customizing how a key is lifted before being passed to addkeyval! in dictlike construction.
By default, calls StructUtils.lift.
Example
struct Point
x::Int; y::Int
end
# lift a Point from a string value
StructUtils.liftkey(::StructUtils.StructStyle, x::String) = Point(parse(Int, split(x, "_")[1]), parse(Int, split(x, "_")[2]))
d = Dict("1_2" => 99)
StructUtils.make(Dict{Point, Int}, Dict("1_2" => 99))
# Dict{Point, Int} with 1 entry:
# Point(1, 2) => 99For loss-less round-tripping also provide a StructUtils.lowerkey overload to "lower" the key.
StructUtils.lower — FunctionStructUtils.lower(x) -> x StructUtils.lower(::StructStyle, x) -> x
Domain value transformation function. This function is called by StructUtils.applyeach on each value in the source object before calling the apply function. By default, lower is the identity function. This allows a domain transformation of values according to the style used.
StructUtils.lowerkey — MethodStructUtils.lowerkey(x) -> x StructUtils.lowerkey(style::StructUtils.StructStyle, x) -> x
Allows customizing how a value is lowered when used specifically as a key. By default, calls StructUtils.lower. Called from StructUtils.applyeach on the key or index before passed to the key-value function.
Example
struct Point
x::Int; y::Int
end
# lower a Point as a single string value
StructUtils.lowerkey(::StructUtils.StructStyle, p::Point) = "$(p.x)_$(p.y)"
d = Dict(Point(1, 2) => 99)
StructUtils.make(Dict{String, Dict{String, Point}}, Dict(Point(1, 2) => Dict(Point(3, 4) => Point(5, 6))))
# Dict{String, Dict{String, Point}} with 1 entry:
# "1_2" => Dict("3_4"=>Point(5, 6))For loss-less round-tripping also provide a StructUtils.liftkey overload to "lift" the key back.
StructUtils.make — FunctionStructUtils.make(T, source) -> T
StructUtils.make(T, source, style) -> T
StructUtils.make(style, T, source) -> Tuple{T, Any}
StructUtils.make!(style, x::T, source)Construct a struct of type T from source using the given style. The source can be any type of object, and the style can be any StructStyle subtype (default StructUtils.DefaultStyle()).
make will use any knowledge of noarg, arraylike, or dictlike in order to determine how to construct an instance of T. The fallback for structs is to rely on the automatic "all argument" constructor that structs have defined by default (e.g. T(fields...)).
make calls applyeach on the source object, where the key-value pairs from source will be used in constructing T.
The 3rd definition takes a style argument, allowing for overloads of non-owned types T. The main difference between this and the 2nd definition is that the 3rd definition allows for the make function to return a tuple of the constructed struct and any side-effect state derived from making the struct.
The 4th definition allows passing in an already-constructed instance of T (x), which must be mutable, and source key-value pairs will be applied as to x as source keys are matched to struct field names.
For structs, fieldtags will be accounted for and certain tags can be used to influence the construction of the struct.
StructUtils.make! — FunctionStructUtils.make(T, source) -> T
StructUtils.make(T, source, style) -> T
StructUtils.make(style, T, source) -> Tuple{T, Any}
StructUtils.make!(style, x::T, source)Construct a struct of type T from source using the given style. The source can be any type of object, and the style can be any StructStyle subtype (default StructUtils.DefaultStyle()).
make will use any knowledge of noarg, arraylike, or dictlike in order to determine how to construct an instance of T. The fallback for structs is to rely on the automatic "all argument" constructor that structs have defined by default (e.g. T(fields...)).
make calls applyeach on the source object, where the key-value pairs from source will be used in constructing T.
The 3rd definition takes a style argument, allowing for overloads of non-owned types T. The main difference between this and the 2nd definition is that the 3rd definition allows for the make function to return a tuple of the constructed struct and any side-effect state derived from making the struct.
The 4th definition allows passing in an already-constructed instance of T (x), which must be mutable, and source key-value pairs will be applied as to x as source keys are matched to struct field names.
For structs, fieldtags will be accounted for and certain tags can be used to influence the construction of the struct.
StructUtils.noarg — FunctionStructUtils.noarg(x) -> Bool StructUtils.noarg(::StructStyle, x) -> Bool StructUtils.noarg(::StructStyle, ::Type{T}) -> Bool
Signals that x or type T is a mutable type that can be constructed by calling an empty constructor, like t = T(). Automatically overloaded when structs use the @noarg macro in their struct definition. The default value is false unless explicitly overloaded.
StructUtils.nulllike — FunctionStructUtils.nulllike(x) -> Bool StructUtils.nulllike(::StructStyle, x) -> Bool StructUtils.nulllike(::StructStyle, ::Type{T}) -> Bool
Returns true if x or type T is null-like, false otherwise. This function is mainly used in the make! implementation to determine if a Union type can be narrowed by excluding nulllike types like Nothing and Missing.
StructUtils.reset! — MethodStructUtils.reset!(x::T)
If T was defined with default values via @defaults, @tags, @kwarg, or @noarg, reset! will reset the fields of x to their default values. T must be a mutable struct type.
StructUtils.structlike — FunctionStructUtils.structlike(x) -> Bool StructUtils.structlike(::StructStyle, x) -> Bool StructUtils.structlike(::StructStyle, ::Type{T}) -> Bool
Returns true if x or type T is struct-like, false otherwise. This function is called by StructUtils.make to determine if T is struct-like. The default implementation returns true for isstructtype(T) and !Base.issingletontype(T).
structlike structs are expected to be able to be constructed by the default constructor like T(field1, field2, ...).
Due to how StructUtils.make works, structlike is often overloaded to false by "unit"/"atom" types where fields should be considered private to the make process and should instead attempt to lift the source object into the unit type.
StructUtils.@choosetype — MacroStructUtils.@choosetype T func
StructUtils.@choosetype style T funcConvenience macro for defining a StructUtils.make! overload for an abstract type T where func is a function that "chooses" a concrete type S at runtime. func can be one of two forms:
source -> S(source, tags) -> S)
That is, it either takes just the source object that is passed to make and must choose a concrete type S, or it can take both the source and a set of fieldtags that may be present for the field of a type being "made".
The 2nd definition also takes a style argument, allowing for overloads of non-owned types T.
Example:
abstract type Vehicle end
struct Car <: Vehicle
make::String
model::String
seatingCapacity::Int
topSpeed::Float64
end
struct Truck <: Vehicle
make::String
model::String
payloadCapacity::Float64
end
StructUtils.@choosetype Vehicle x -> x["type"] == "car" ? Car : x["type"] == "truck" ? Truck : throw(ArgumentError("Unknown vehicle type: $(x["type"])"))
x = StructUtils.make(Vehicle, Dict("type" => "car", "make" => "Toyota", "model" => "Corolla", "seatingCapacity" => 4, "topSpeed" => 120.5))
@test x == Car("Toyota", "Corolla", 4, 120.5)StructUtils.@defaults — Macro@defaults struct T
...
endMacro to enhance a struct definition by automatically generating an outer constructor with default values for trailing fields. The generated constructor will accept arguments for non-default fields and pass default values to the inner constructor. StructUtils.fielddefaults trait is also overridden to return a NamedTuple of default values for the struct type.
The @noarg, @kwarg, @defaults, and @tags macros all support specifying "field tags" for each field in a struct. Field tags are a NamedTuple prefixed by & and are a way to attach metadata to a field. The field tags are accessible via the StructUtils.fieldtags function, and certain field tags are used by the StructUtils.make function to control how fields are constructed, including:
dateformat: aDateFormatobject to use when parsing or formatting aDates.TimeTypefieldlower: a function to apply to a field whenapplyeachis called on a structlift: a function to apply to a field whenStructUtils.makeis called on a structignore: aBoolto indicate if a field should be skipped/ignored whenapplyeachormakeis calledname: aSymbolto be used instead of a defined field name inapplyeachor used to match a field inmakechoosetype: a function to apply to a field whenStructUtils.makeis called to determine the concrete type of an abstract or Union typed field
For example, the following struct definition includes a field with a dateformat tag:
@tags struct MyStruct
date::Date &(dateformat=dateformat"yyyy-mm-dd",)
endExample
@defaults struct Foo
a::Int
b::String = "foo"
c::Float64 = 1.0
d::Vector{Int} = [1, 2, 3]
endIn the above example, the @defaults macro generates the following outer constructor:
function Foo(a)
return Foo(a, "foo", 1.0, [1, 2, 3])
endStructUtils.@kwarg — Macro@kwarg struct T
...
endMacro to enhance a struct definition by automatically generating a keyword argument constructor. Default values can be specified for fields, which will be set in the generated constructor. StructUtils.kwarg trait is also overridden to return true for the struct type. This allows structs to easily participate in programmatic construction via StructUtils.make.
The @noarg, @kwarg, @defaults, and @tags macros all support specifying "field tags" for each field in a struct. Field tags are a NamedTuple prefixed by & and are a way to attach metadata to a field. The field tags are accessible via the StructUtils.fieldtags function, and certain field tags are used by the StructUtils.make function to control how fields are constructed, including:
dateformat: aDateFormatobject to use when parsing or formatting aDates.TimeTypefieldlower: a function to apply to a field whenapplyeachis called on a structlift: a function to apply to a field whenStructUtils.makeis called on a structignore: aBoolto indicate if a field should be skipped/ignored whenapplyeachormakeis calledname: aSymbolto be used instead of a defined field name inapplyeachor used to match a field inmakechoosetype: a function to apply to a field whenStructUtils.makeis called to determine the concrete type of an abstract or Union typed field
For example, the following struct definition includes a field with a dateformat tag:
@tags struct MyStruct
date::Date &(dateformat=dateformat"yyyy-mm-dd",)
endExample
@kwarg struct Foo
a::Int
b::String = "foo"
c::Float64 = 1.0
d::Vector{Int} = [1, 2, 3]
endIn the above example, the @kwarg macro generates the following inner constructor:
function Foo(; a, b="foo", c=1.0, d=[1, 2, 3])
return Foo(a, b, c, d)
endStructUtils.@noarg — Macro@noarg mutable struct T
...
endMacro to enhance a mutable struct definition by automatically generating an empty or "no-argument" constructor. Similar to the @kwarg macro, default values can be specified for fields, which will be set in the generated constructor. StructUtils.noarg trait is also overridden to return true for the struct type. This allows structs to easily participate in programmatic construction via StructUtils.make.
Note that const fields are currently not allowed in @noarg structs.
The @noarg, @kwarg, @defaults, and @tags macros all support specifying "field tags" for each field in a struct. Field tags are a NamedTuple prefixed by & and are a way to attach metadata to a field. The field tags are accessible via the StructUtils.fieldtags function, and certain field tags are used by the StructUtils.make function to control how fields are constructed, including:
dateformat: aDateFormatobject to use when parsing or formatting aDates.TimeTypefieldlower: a function to apply to a field whenapplyeachis called on a structlift: a function to apply to a field whenStructUtils.makeis called on a structignore: aBoolto indicate if a field should be skipped/ignored whenapplyeachormakeis calledname: aSymbolto be used instead of a defined field name inapplyeachor used to match a field inmakechoosetype: a function to apply to a field whenStructUtils.makeis called to determine the concrete type of an abstract or Union typed field
For example, the following struct definition includes a field with a dateformat tag:
@tags struct MyStruct
date::Date &(dateformat=dateformat"yyyy-mm-dd",)
endExample
@noarg mutable struct Foo
a::Int
b::String
c::Float64 = 1.0
d::Vector{Int} = [1, 2, 3]
endIn the above example, the @noarg macro generates the following inner constructor:
function Foo()
x = new()
x.c = 1.0
x.d = [1, 2, 3]
return x
endStructUtils.@nonstruct — Macro@nonstruct struct T
...
endMacro to mark a struct as not struct-like for StructUtils purposes. This macro overrides StructUtils.structlike to return false for the struct type, which means that StructUtils.make will not attempt to construct the struct using its fields, but will instead use the lift function to convert the source directly to the struct type.
This is useful for "unit" or "atom" types where the fields should be considered private to the make process and the struct should be constructed by lifting the source object directly.
Note: The @nonstruct macro does not support field defaults, field tags, or other StructUtils macros (@defaults, @tags, @noarg, @kwarg) because by using @nonstruct, you are explicitly opting out of StructUtils' struct-like functionality. The struct's fields are considered private implementation details for the make process.
Example
@nonstruct struct MyUnit
value::String
end
# This will use StructUtils.lift to convert the source directly to MyUnit
# rather than trying to construct it from field values
x = StructUtils.make(MyUnit, "hello")StructUtils.@tags — Macro@tags struct T
...
endMacro to enhance a struct definition by allowing field tags to be specified for each field.
The @noarg, @kwarg, @defaults, and @tags macros all support specifying "field tags" for each field in a struct. Field tags are a NamedTuple prefixed by & and are a way to attach metadata to a field. The field tags are accessible via the StructUtils.fieldtags function, and certain field tags are used by the StructUtils.make function to control how fields are constructed, including:
dateformat: aDateFormatobject to use when parsing or formatting aDates.TimeTypefieldlower: a function to apply to a field whenapplyeachis called on a structlift: a function to apply to a field whenStructUtils.makeis called on a structignore: aBoolto indicate if a field should be skipped/ignored whenapplyeachormakeis calledname: aSymbolto be used instead of a defined field name inapplyeachor used to match a field inmakechoosetype: a function to apply to a field whenStructUtils.makeis called to determine the concrete type of an abstract or Union typed field
For example, the following struct definition includes a field with a dateformat tag:
@tags struct MyStruct
date::Date &(dateformat=dateformat"yyyy-mm-dd",)
endStructUtils.Selectors — ModuleSelection syntaxSpecial "selection syntax" is provided that allows easy querying of objects/arrays that implement StructUtils.applyeach using a syntax similar to XPath or CSS selectors, applied using common Julia syntax.
This syntax mainly uses various forms of getindex to select elements of an object or array. Supported syntax includes:
x["key"]/x.key/x[:key]/x[1]- select the value associated for a key in objectx(key can be a String, Symbol, or Integer for an array)x[:]- select all values in object or arrayx, returned as aSelectors.List, which is a custom array type that supports the selection syntaxx.key- whenxis aList, select the value forkeyin each element of theList(like a broadcastedgetindex)x[~, key]- recursively select all values in object or arrayxthat havekeyx[~, :]- recursively select all values in object or arrayx, returned as a flattenedListx[:, (k, v) -> Bool]- apply a key-value functionfto each key-value/index-value in object or arrayx, and return aListof all values for whichfreturnstrue
StructUtils.Selectors.List — TypeList(...)A custom array wrapper that supports the Selectors selection syntax.