Skip to content

Commit

Permalink
allow printing some TOML dictionaries inline by marking them with an …
Browse files Browse the repository at this point in the history
…IdSet (#53233)

This was one way I came up with to "mark" what dictionaries one wants to
print inline. I am not sure `IdSet` is even considered public but it
seemed the most reasonable for this.
  • Loading branch information
KristofferC committed Feb 8, 2024
1 parent 4c2df21 commit e68e432
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 21 deletions.
5 changes: 3 additions & 2 deletions stdlib/TOML/src/TOML.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ const ParserError = Internals.ParserError


"""
print([to_toml::Function], io::IO [=stdout], data::AbstractDict; sorted=false, by=identity)
print([to_toml::Function], io::IO [=stdout], data::AbstractDict; sorted=false, by=identity, inline_tables::Base.IdSet{<:AbstractDict})
Write `data` as TOML syntax to the stream `io`. If the keyword argument `sorted` is set to `true`,
sort tables according to the function given by the keyword argument `by`.
sort tables according to the function given by the keyword argument `by`. If the keyword argument
`inline_tables` is given, it should be a set of tables that should be printed "inline".
The following data types are supported: `AbstractDict`, `AbstractVector`, `AbstractString`, `Integer`, `AbstractFloat`, `Bool`,
`Dates.DateTime`, `Dates.Time`, `Dates.Date`. Note that the integers and floats
Expand Down
52 changes: 33 additions & 19 deletions stdlib/TOML/src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,21 @@ end
##########

# Fallback
function printvalue(f::MbyFunc, io::IO, value)
function printvalue(f::MbyFunc, io::IO, value, sorted::Bool)
toml_value = to_toml_value(f, value)
@invokelatest printvalue(f, io, toml_value)
end

function printvalue(f::MbyFunc, io::IO, value::AbstractVector)
function printvalue(f::MbyFunc, io::IO, value::AbstractVector, sorted::Bool)
Base.print(io, "[")
for (i, x) in enumerate(value)
i != 1 && Base.print(io, ", ")
printvalue(f, io, x)
printvalue(f, io, x, sorted)
end
Base.print(io, "]")
end

function printvalue(f::MbyFunc, io::IO, value::TOMLValue)
function printvalue(f::MbyFunc, io::IO, value::TOMLValue, sorted::Bool)
value isa Dates.DateTime ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z")) :
value isa Dates.Time ? Base.print(io, Dates.format(value, Dates.dateformat"HH:MM:SS.sss")) :
value isa Dates.Date ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd")) :
Expand All @@ -100,7 +100,7 @@ function printvalue(f::MbyFunc, io::IO, value::TOMLValue)
value isa AbstractString ? (Base.print(io, "\"");
print_toml_escaped(io, value);
Base.print(io, "\"")) :
value isa AbstractDict ? print_inline_table(f, io, value) :
value isa AbstractDict ? print_inline_table(f, io, value, sorted) :
error("internal error in TOML printing, unhandled value")
end

Expand All @@ -112,13 +112,18 @@ function print_integer(io::IO, value::Integer)
return
end

function print_inline_table(f::MbyFunc, io::IO, value::AbstractDict)
function print_inline_table(f::MbyFunc, io::IO, value::AbstractDict, sorted::Bool)
vkeys = collect(keys(value))
if sorted
sort!(vkeys)
end
Base.print(io, "{")
for (i, (k,v)) in enumerate(value)
for (i, k) in enumerate(vkeys)
v = value[k]
i != 1 && Base.print(io, ", ")
printkey(io, [String(k)])
Base.print(io, " = ")
printvalue(f, io, v)
printvalue(f, io, v, sorted)
end
Base.print(io, "}")
end
Expand All @@ -141,11 +146,18 @@ function print_table(f::MbyFunc, io::IO, a::AbstractDict,
indent::Int = 0,
first_block::Bool = true,
sorted::Bool = false,
inline_tables::Base.IdSet,
by::Function = identity,
)

if a in inline_tables
@invokelatest print_inline_table(f, io, a)
return
end

akeys = keys(a)
if sorted
akeys = sort!(collect(akeys); by=by)
akeys = sort!(collect(akeys); by)
end

# First print non-tabular entries
Expand All @@ -154,12 +166,14 @@ function print_table(f::MbyFunc, io::IO, a::AbstractDict,
if !isa(value, TOMLValue)
value = to_toml_value(f, value)
end
is_tabular(value) && continue
if is_tabular(value) && !(value in inline_tables)
continue
end

Base.print(io, ' '^4max(0,indent-1))
printkey(io, [String(key)])
Base.print(io, " = ") # print separator
printvalue(f, io, value)
printvalue(f, io, value, sorted)
Base.print(io, "\n") # new line?
first_block = false
end
Expand All @@ -169,10 +183,10 @@ function print_table(f::MbyFunc, io::IO, a::AbstractDict,
if !isa(value, TOMLValue)
value = to_toml_value(f, value)
end
if is_table(value)
if is_table(value) && !(value in inline_tables)
push!(ks, String(key))
_values = @invokelatest values(value)
header = isempty(value) || !all(is_tabular(v) for v in _values)::Bool
header = isempty(value) || !all(is_tabular(v) for v in _values)::Bool || any(v in inline_tables for v in _values)::Bool
if header
# print table
first_block || println(io)
Expand All @@ -183,7 +197,7 @@ function print_table(f::MbyFunc, io::IO, a::AbstractDict,
Base.print(io,"]\n")
end
# Use runtime dispatch here since the type of value seems not to be enforced other than as AbstractDict
@invokelatest print_table(f, io, value, ks; indent = indent + header, first_block = header, sorted=sorted, by=by)
@invokelatest print_table(f, io, value, ks; indent = indent + header, first_block = header, sorted, by, inline_tables)
pop!(ks)
elseif @invokelatest(is_array_of_tables(value))
# print array of tables
Expand All @@ -197,7 +211,7 @@ function print_table(f::MbyFunc, io::IO, a::AbstractDict,
Base.print(io,"]]\n")
# TODO, nicer error here
!isa(v, AbstractDict) && error("array should contain only tables")
@invokelatest print_table(f, io, v, ks; indent = indent + 1, sorted=sorted, by=by)
@invokelatest print_table(f, io, v, ks; indent = indent + 1, sorted, by, inline_tables)
end
pop!(ks)
end
Expand All @@ -209,7 +223,7 @@ end
# API #
#######

print(f::MbyFunc, io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = print_table(f, io, a; sorted=sorted, by=by)
print(f::MbyFunc, a::AbstractDict; sorted::Bool=false, by=identity) = print(f, stdout, a; sorted=sorted, by=by)
print(io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = print_table(nothing, io, a; sorted=sorted, by=by)
print(a::AbstractDict; sorted::Bool=false, by=identity) = print(nothing, stdout, a; sorted=sorted, by=by)
print(f::MbyFunc, io::IO, a::AbstractDict; sorted::Bool=false, by=identity, inline_tables::Base.IdSet{<:AbstractDict}=Base.IdSet{Dict{String}}()) = print_table(f, io, a; sorted, by, inline_tables)
print(f::MbyFunc, a::AbstractDict; sorted::Bool=false, by=identity, inline_tables::Base.IdSet{<:AbstractDict}=Base.IdSet{Dict{String}}()) = print(f, stdout, a; sorted, by, inline_tables)
print(io::IO, a::AbstractDict; sorted::Bool=false, by=identity, inline_tables::Base.IdSet{<:AbstractDict}=Base.IdSet{Dict{String}}()) = print_table(nothing, io, a; sorted, by, inline_tables)
print( a::AbstractDict; sorted::Bool=false, by=identity, inline_tables::Base.IdSet{<:AbstractDict}=Base.IdSet{Dict{String}}()) = print(nothing, stdout, a; sorted, by, inline_tables)
55 changes: 55 additions & 0 deletions stdlib/TOML/test/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,58 @@ d = "hello"
a = 2
b = 9.9
"""


inline_dict = Dict("a" => [1,2], "b" => Dict("a" => "b"), "c" => "foo")
d = Dict(
"x" => "y",
"y" => inline_dict,
"z" => [1,2,3],
)
inline_tables = Base.IdSet{Dict}()
push!(inline_tables, inline_dict)
@test toml_str(d; sorted=true, inline_tables) ==
"""
x = "y"
y = {a = [1, 2], b = {a = "b"}, c = "foo"}
z = [1, 2, 3]
"""


d = Dict("deps" => Dict(
"LocalPkg" => "fcf55292-0d03-4e8a-9e0b-701580031fc3",
"Example" => "7876af07-990d-54b4-ab0e-23690620f79a"),
"sources" => Dict(
"LocalPkg" => Dict("path" => "LocalPkg"),
"Example" => Dict("url" => "https://github.com/JuliaLang/Example.jl")))

inline_tables = Base.IdSet{Dict}()
push!(inline_tables, d["sources"]["LocalPkg"])
push!(inline_tables, d["sources"]["Example"])

@test toml_str(d; sorted=true, inline_tables) ==
"""
[deps]
Example = "7876af07-990d-54b4-ab0e-23690620f79a"
LocalPkg = "fcf55292-0d03-4e8a-9e0b-701580031fc3"
[sources]
Example = {url = "https://github.com/JuliaLang/Example.jl"}
LocalPkg = {path = "LocalPkg"}
"""

inline_tables = Base.IdSet{Dict}()
push!(inline_tables, d["sources"]["LocalPkg"])
s = """
[deps]
Example = "7876af07-990d-54b4-ab0e-23690620f79a"
LocalPkg = "fcf55292-0d03-4e8a-9e0b-701580031fc3"
[sources]
LocalPkg = {path = "LocalPkg"}
[sources.Example]
url = "https://github.com/JuliaLang/Example.jl"
"""
@test toml_str(d; sorted=true, inline_tables) == s
@test roundtrip(s)

0 comments on commit e68e432

Please sign in to comment.