From f4059b63bbf0482f1d7b3289c4d05f08ba4d6353 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 11 Mar 2026 19:00:27 -0400 Subject: [PATCH 1/2] Implement a registration system for chunk key encoding --- src/chunkkeyencoding.jl | 55 +++++++++++++++++++++++++++++++++++++++++ src/metadata.jl | 2 +- src/metadata3.jl | 19 -------------- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/chunkkeyencoding.jl b/src/chunkkeyencoding.jl index a6b5ab5e..e12ed255 100644 --- a/src/chunkkeyencoding.jl +++ b/src/chunkkeyencoding.jl @@ -65,4 +65,59 @@ function lower_chunk_key_encoding(e::SuffixChunkKeyEncoding) ) end +""" +Registry mapping chunk key encoding names to parser functions. + +Each parser function takes a configuration `Dict{String,Any}` and returns an +`AbstractChunkKeyEncoding`. Use `register_chunk_key_encoding` to add new entries. +""" +const chunk_key_encoding_parsers = Dict{String, Function}() + +""" + register_chunk_key_encoding(parser::Function, name::String) + +Register a chunk key encoding parser under `name`. The parser must accept a +`Dict{String,Any}` configuration and return an `AbstractChunkKeyEncoding`. + +Supports do-block syntax: + + register_chunk_key_encoding("myenc") do config + MyEncoding(config["param"]) + end +""" +function register_chunk_key_encoding(parser::Function, name::String) + chunk_key_encoding_parsers[name] = parser +end + +""" + parse_chunk_key_encoding(d::AbstractDict) -> AbstractChunkKeyEncoding + +Parse a chunk key encoding dict (as found in `zarr.json`) into an +`AbstractChunkKeyEncoding` by looking up the registered parser for the encoding name. +""" +function parse_chunk_key_encoding(d::AbstractDict)::AbstractChunkKeyEncoding + name = d["name"] + config = get(d, "configuration", Dict{String,Any}())::Dict{String,Any} + if haskey(chunk_key_encoding_parsers, name) + return chunk_key_encoding_parsers[name](config) + else + throw(ArgumentError("Unknown chunk_key_encoding of name, $name")) + end +end + +# Register built-in encodings +register_chunk_key_encoding("default") do config + ChunkKeyEncoding(only(get(config, "separator", '/')), true) +end + +register_chunk_key_encoding("v2") do config + ChunkKeyEncoding(only(get(config, "separator", '.')), false) +end + +register_chunk_key_encoding("suffix") do config + suffix_str = config["suffix"] + base = parse_chunk_key_encoding(config["base_encoding"]) + SuffixChunkKeyEncoding(suffix_str, base) +end + _concatpath(p,s) = isempty(p) ? s : rstrip(p,'/') * '/' * s diff --git a/src/metadata.jl b/src/metadata.jl index ee584720..7b36bbad 100644 --- a/src/metadata.jl +++ b/src/metadata.jl @@ -191,7 +191,7 @@ function Metadata(A::AbstractArray{T,N}, chunks::NTuple{N,Int}, ::ZarrFormat{2}; ) end -Metadata(s::Union{AbstractString, IO}, fill_as_missing) = Metadata(JSON.parse(s; dicttype=Dict), fill_as_missing) +Metadata(s::Union{AbstractString, IO}, fill_as_missing) = Metadata(JSON.parse(s; dicttype=Dict{String,Any}), fill_as_missing) "Construct Metadata from Dict" function Metadata(d::AbstractDict, fill_as_missing) diff --git a/src/metadata3.jl b/src/metadata3.jl index 75ad6e6e..918e15ef 100644 --- a/src/metadata3.jl +++ b/src/metadata3.jl @@ -151,22 +151,6 @@ function get_order(md::MetadataV3) end get_order(md::MetadataV2) = md.order -"""Parse a chunk_key_encoding dict into an `AbstractChunkKeyEncoding` value.""" -function parse_chunk_key_encoding(d::AbstractDict) - name = d["name"] - config = get(d, "configuration", Dict{String,Any}()) - if name == "default" - return ChunkKeyEncoding(only(get(config, "separator", '/')), true) - elseif name == "v2" - return ChunkKeyEncoding(only(get(config, "separator", '.')), false) - elseif name == "suffix" - suffix_str = config["suffix"] - base = parse_chunk_key_encoding(config["base_encoding"]) - return SuffixChunkKeyEncoding(suffix_str, base) - else - throw(ArgumentError("Unknown chunk_key_encoding of name, $name")) - end -end function Metadata3(d::AbstractDict, fill_as_missing) @@ -238,9 +222,6 @@ function Metadata3(d::AbstractDict, fill_as_missing) # Chunk Key Encoding chunk_key_encoding = d["chunk_key_encoding"] - if chunk_key_encoding["name"] ∉ ("default", "v2", "suffix") - throw(ArgumentError("Unknown chunk_key_encoding of name, $(chunk_key_encoding["name"])")) - end # Build V3Pipeline from codec chain array_array_codecs = [] From 9441c6d9a021319274bf3eeb2f61b9a799175951 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Fri, 13 Mar 2026 15:54:25 -0400 Subject: [PATCH 2/2] refactor: Add typed ChunkKeyEncodingEntry to chunk key encoding registration Introduce `ChunkKeyEncodingEntry` struct pairing `return_type::Type{<:AbstractChunkKeyEncoding}` with `parser::Function`, replacing the bare `Dict{String, Function}` registry. - `register_chunk_key_encoding(parser, name, ::Type{T}=AbstractChunkKeyEncoding)` now stores a `ChunkKeyEncodingEntry` with the declared return type; the type is the last (optional) argument to work naturally with do-block syntax - `parse_chunk_key_encoding` asserts the result against `entry.return_type` at runtime - All built-in registrations updated with their specific concrete types (`ChunkKeyEncoding` for "default" and "v2", `SuffixChunkKeyEncoding` for "suffix") Co-Authored-By: Claude Sonnet 4.6 --- src/chunkkeyencoding.jl | 44 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/chunkkeyencoding.jl b/src/chunkkeyencoding.jl index e12ed255..9a9be200 100644 --- a/src/chunkkeyencoding.jl +++ b/src/chunkkeyencoding.jl @@ -65,29 +65,44 @@ function lower_chunk_key_encoding(e::SuffixChunkKeyEncoding) ) end -""" -Registry mapping chunk key encoding names to parser functions. +"""Stores a registered chunk key encoding parser together with its expected return type.""" +struct ChunkKeyEncodingEntry + return_type::Type{<:AbstractChunkKeyEncoding} + parser::Function +end -Each parser function takes a configuration `Dict{String,Any}` and returns an -`AbstractChunkKeyEncoding`. Use `register_chunk_key_encoding` to add new entries. """ -const chunk_key_encoding_parsers = Dict{String, Function}() +Registry mapping chunk key encoding names to `ChunkKeyEncodingEntry` values +(return type + parser function). Use `register_chunk_key_encoding` to add new entries. +""" +const chunk_key_encoding_parsers = Dict{String, ChunkKeyEncodingEntry}() """ - register_chunk_key_encoding(parser::Function, name::String) + register_chunk_key_encoding(parser::Function, name::String[, ::Type{T}]) Register a chunk key encoding parser under `name`. The parser must accept a `Dict{String,Any}` configuration and return an `AbstractChunkKeyEncoding`. +The optional trailing `Type{T}` argument narrows the declared return type stored +in the registry (defaults to `AbstractChunkKeyEncoding`). Specifying it enables +a runtime assertion in `parse_chunk_key_encoding` and makes the registry +self-documenting. + Supports do-block syntax: register_chunk_key_encoding("myenc") do config MyEncoding(config["param"]) end + + register_chunk_key_encoding("myenc", MyEncoding) do config + MyEncoding(config["param"]) + end """ -function register_chunk_key_encoding(parser::Function, name::String) - chunk_key_encoding_parsers[name] = parser +function register_chunk_key_encoding(parser::Function, name::String, ::Type{T}) where {T<:AbstractChunkKeyEncoding} + chunk_key_encoding_parsers[name] = ChunkKeyEncodingEntry(T, parser) end +register_chunk_key_encoding(parser::Function, name::String) = + register_chunk_key_encoding(parser, name, AbstractChunkKeyEncoding) """ parse_chunk_key_encoding(d::AbstractDict) -> AbstractChunkKeyEncoding @@ -98,23 +113,22 @@ Parse a chunk key encoding dict (as found in `zarr.json`) into an function parse_chunk_key_encoding(d::AbstractDict)::AbstractChunkKeyEncoding name = d["name"] config = get(d, "configuration", Dict{String,Any}())::Dict{String,Any} - if haskey(chunk_key_encoding_parsers, name) - return chunk_key_encoding_parsers[name](config) - else + haskey(chunk_key_encoding_parsers, name) || throw(ArgumentError("Unknown chunk_key_encoding of name, $name")) - end + entry = chunk_key_encoding_parsers[name] + return entry.parser(config)::entry.return_type end # Register built-in encodings -register_chunk_key_encoding("default") do config +register_chunk_key_encoding("default", ChunkKeyEncoding) do config ChunkKeyEncoding(only(get(config, "separator", '/')), true) end -register_chunk_key_encoding("v2") do config +register_chunk_key_encoding("v2", ChunkKeyEncoding) do config ChunkKeyEncoding(only(get(config, "separator", '.')), false) end -register_chunk_key_encoding("suffix") do config +register_chunk_key_encoding("suffix", SuffixChunkKeyEncoding) do config suffix_str = config["suffix"] base = parse_chunk_key_encoding(config["base_encoding"]) SuffixChunkKeyEncoding(suffix_str, base)