Skip to content

Commit 828ce32

Browse files
authored
Merge pull request #2 from JuliaDocs/new-toml-header
Revise TOML format
2 parents 64f349a + e23ec34 commit 828ce32

7 files changed

Lines changed: 196 additions & 43 deletions

File tree

.typos.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[default]
2+
extend-ignore-identifiers-re = [
3+
"[A-Z]T",
4+
"[0-9abcdef]{8}",
5+
"[A-Z][a-z]+[A-Z]{2,5}[0-9]{4}",
6+
]
7+
8+
[files]
9+
extend-exclude = ["*.bib", "*.toml", "test_inventory.jl"]

docs/src/formats.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,24 @@ Note that `DocInventories` internally uses the `text/x-intersphinx` MIME type fo
3535

3636
## TOML Format
3737

38-
The TOML format is another text output format that is optimized for human readability. It starts with a header section of the form
38+
The TOML format is a text output format that is optimized for human readability. It starts with a header section of the form
3939

40-
```
41-
[Inventory]
42-
format = "DocInventories v0"
40+
```toml
41+
# DocInventory version 0
4342
project = "<project>"
4443
version = "<version>"
4544
```
4645

47-
The `format` line is mandatory and identifies the file as containing inventory data in the format described here.
46+
The comment in the first line is mandatory and identifies the file as containing inventory data in the format described here.
4847

4948
!!! warning
50-
As indicated by the `v0` in the `format` line, the format described here is currently experimental and may change without notice
49+
As indicated by the "version 0" in the header comment line, the format described here is currently experimental and may change without notice
50+
51+
The `project` line must specify the name of the project described by the inventory. It is mandatory. The `version` line may specify the version of the project. It is optional, but recommended.
5152

5253
After that, each [`InventoryItem`](@ref) is represented by a multi-line block of the form
5354

54-
```
55+
```toml
5556
[[<domain>.<role>]]
5657
name = "<name>"
5758
uri = "<uri>"
@@ -61,7 +62,7 @@ priority = <priority>
6162

6263
The four lines for `name`, `uri`, `dispname`, and `priority` may occur in any order. Also, for items with the default priority (`-1` for the `std` domain, `1` otherwise), the `priority` line may be omitted. If `dispname` is equal to `name` (usually indicated by `dispname="-"`), the `dispname` line may also be omitted.
6364

64-
The item-blocks may be grouped/separated by blank lines. In `.toml` file generated by `DocInventories.save("inventory.toml", inventory)` items will be grouped into blocks with the same `[[<domain>.<role>]]` with a blank line between each block.
65+
The item-blocks may be grouped/separated by blank lines. In `.toml` files generated by `DocInventories.save("inventory.toml", inventory)` items will be grouped into blocks with the same `[[<domain>.<role>]]` with a blank line between each block.
6566

6667
Any TOML parser should read a `.toml` file with the above structure into a nested dictionary, so that `item_dict = toml_data[domain][role][i]` corresponds to the `i`'th inventory item with the given `domain` and `role`. That `item_dict` will then map `"name"`, `"uri"`, and potentially `"dispname"` and `"priority"` to their respective values.
6768

docs/src/inventories/Documenter.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
[Inventory]
2-
format = "DocInventories v0"
1+
# DocInventory version 0
32
project = "Documenter.jl"
43
version = "1.2.1"
54

docs/src/inventories/Julia.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
[Inventory]
2-
format = "DocInventories v0"
1+
# DocInventory version 0
2+
33
project = "The Julia Language"
44
version = "1.10.0"
55

docs/src/inventories/JuliaDocs.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
[Inventory]
2-
format = "DocInventories v0"
1+
# DocInventory version 0
32
project = "JuliaDocs"
4-
version = ""
53

64
[[std.doc]]
75
name = "DocumenterInterLinks"

src/toml_format.jl

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,47 +25,50 @@ function Base.show(io::IO, ::MIME"application/toml", inventory::Inventory)
2525
push!(domains[item.domain][item.role], item_data)
2626
end
2727

28-
toml_dict = Dict(
29-
"Inventory" => Dict(
30-
"format" => "DocInventories v0",
31-
"project" => inventory.project,
32-
"version" => inventory.version,
33-
),
34-
domains...
35-
)
28+
toml_dict = Dict("project" => inventory.project, domains...)
29+
if !isempty(inventory.version)
30+
toml_dict["version"] = inventory.version
31+
end
3632

33+
TOML.println(io, "# DocInventory version 0")
3734
TOML.print(io, toml_dict; sorted=true)
3835

3936
end
4037

4138

4239
function read_inventory(io::IO, ::MIME"application/toml")
40+
format_header = readline(io)
41+
if !startswith(format_header, "#")
42+
@warn "Invalid format_header: $format_header"
43+
# TODO: verify format_header more strictly once the format has settled.
44+
end
4345
data = TOML.parse(io)
4446
try
45-
inventory_format = data["Inventory"]["format"]
46-
if inventory_format != "DocInventories v0"
47-
msg = "Invalid inventory format: $(repr(inventory_format))"
48-
throw(InventoryFormatError(msg))
49-
end
50-
project = data["Inventory"]["project"]
51-
version = data["Inventory"]["version"]
47+
project = string(pop!(data, "project")) # mandatory
48+
version = string(pop!(data, "version", "")) # optional
5249
items = InventoryItem[]
5350
for (domain, domain_data) in data
54-
(domain == "Inventory") && continue # that's the header, not a domain
55-
for (role, role_data) in domain_data
56-
for item_data in role_data
57-
push!(
58-
items,
59-
InventoryItem(
60-
item_data["name"],
61-
domain,
62-
role,
63-
get(item_data, "priority", (domain == "std") ? -1 : 1),
64-
item_data["uri"],
65-
get(item_data, "dispname", "-")
51+
if domain_data isa Dict
52+
for (role, role_data) in domain_data
53+
for item_data in role_data
54+
push!(
55+
items,
56+
InventoryItem(
57+
item_data["name"],
58+
domain,
59+
role,
60+
get(item_data, "priority", (domain == "std") ? -1 : 1),
61+
item_data["uri"],
62+
get(item_data, "dispname", "-")
63+
)
6664
)
67-
)
65+
end
6866
end
67+
elseif domain == "format"
68+
# For backward-compatibility, remove this `elseif` in v1.0
69+
@warn "Unexpected key: $domain"
70+
else
71+
throw(InventoryFormatError("Unexpected key: $domain"))
6972
end
7073
end
7174
return project, version, items

test/test_inventory.jl

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,118 @@ end
181181
@test c.value isa InventoryFormatError
182182
@test contains(c.output, "Unexpected line")
183183

184+
filename = joinpath(tempdir, "missing_project.toml")
185+
#!format: off
186+
write(filename, """
187+
# DocInventory version 0
188+
189+
[[std.doc]]
190+
name = "DocumenterInterLinks"
191+
uri = "DocumenterInterLinks.jl#readme"
192+
""")
193+
#!format: on
194+
c = IOCapture.capture(rethrow=Union{}) do
195+
Inventory(filename; root_url="")
196+
end
197+
@test c.value isa InventoryFormatError
198+
@test contains(c.output, "key \"project\" not found")
199+
200+
filename = joinpath(tempdir, "old_format.toml")
201+
#!format: off
202+
write(filename, """
203+
[Inventory]
204+
format = "DocInventories v0"
205+
project = "Test"
206+
version = "0.1.0"
207+
208+
[[std.doc]]
209+
name = "DocumenterInterLinks"
210+
uri = "DocumenterInterLinks.jl#readme"
211+
""")
212+
#!format: on
213+
c = IOCapture.capture(rethrow=Union{}) do
214+
Inventory(filename; root_url="")
215+
end
216+
@test_broken c.value isa InventoryFormatError
217+
@test contains(c.output, "Unexpected key: format")
218+
@test contains(c.output, "Invalid format_header: [Inventory]")
219+
220+
filename = joinpath(tempdir, "old_format2.toml")
221+
#!format: off
222+
write(filename, """
223+
# This is an old inventory file, and because of this comment, the
224+
# `[Inventory]` won't be stripped as a format_header
225+
226+
[Inventory]
227+
format = "DocInventories v0"
228+
project = "Test"
229+
version = "0.1.0"
230+
231+
[[std.doc]]
232+
name = "DocumenterInterLinks"
233+
uri = "DocumenterInterLinks.jl#readme"
234+
""")
235+
#!format: on
236+
c = IOCapture.capture(rethrow=Union{}) do
237+
Inventory(filename; root_url="")
238+
end
239+
@test c.value isa InventoryFormatError
240+
@test contains(c.output, "key \"project\" not found")
241+
242+
filename = joinpath(tempdir, "no_header_line.toml")
243+
#!format: off
244+
write(filename, """
245+
project = "Test"
246+
version = "0.1.0"
247+
248+
[[std.doc]]
249+
name = "DocumenterInterLinks"
250+
uri = "DocumenterInterLinks.jl#readme"
251+
""")
252+
#!format: on
253+
c = IOCapture.capture(rethrow=Union{}) do
254+
Inventory(filename; root_url="")
255+
end
256+
@test c.value isa InventoryFormatError
257+
@test contains(c.output, "Invalid format_header: project = \"Test\"")
258+
@test contains(c.output, "key \"project\" not found")
259+
260+
filename = joinpath(tempdir, "typo1.toml")
261+
#!format: off
262+
write(filename, """
263+
# DocInventory version 0
264+
project = "Test"
265+
verison = "0.1.0"
266+
267+
[[std.doc]]
268+
name = "DocumenterInterLinks"
269+
uri = "DocumenterInterLinks.jl#readme"
270+
""")
271+
#!format: on
272+
c = IOCapture.capture(rethrow=Union{}) do
273+
Inventory(filename; root_url="")
274+
end
275+
@test c.value isa InventoryFormatError
276+
@test contains(c.output, "Unexpected key: verison")
277+
278+
filename = joinpath(tempdir, "typo2.toml")
279+
#!format: off
280+
write(filename, """
281+
# DocInventory version 0
282+
project = "Test"
283+
version = "0.1.0"
284+
285+
[[std_doc]]
286+
name = "DocumenterInterLinks"
287+
uri = "DocumenterInterLinks.jl#readme"
288+
""")
289+
#!format: on
290+
c = IOCapture.capture(rethrow=Union{}) do
291+
Inventory(filename; root_url="")
292+
end
293+
@test c.value isa InventoryFormatError
294+
@test contains(c.output, "Unexpected key: std_doc")
295+
184296
end
185297

186298
end
@@ -484,6 +596,37 @@ end
484596
@test contains(c.output, "Only v2 objects.inv files currently supported")
485597
@test c.value isa InventoryFormatError
486598

599+
filename = joinpath(tempdir, "wrong_version_type.toml")
600+
#!format: off
601+
write(filename, """
602+
# DocInventory version 0
603+
project = "Test"
604+
version = 1.0
605+
606+
[[std.doc]]
607+
name = "DocumenterInterLinks"
608+
uri = "DocumenterInterLinks.jl#readme"
609+
""")
610+
#!format: on
611+
readinv = Inventory(filename; root_url="")
612+
# this still works because the float "1.0" is converted to string
613+
@test readinv.version == "1.0"
614+
615+
filename = joinpath(tempdir, "future_header.toml")
616+
#!format: off
617+
write(filename, """
618+
# Documenter Inventory version 1
619+
project = "Test"
620+
version = "1.0"
621+
622+
[[std.doc]]
623+
name = "DocumenterInterLinks"
624+
uri = "DocumenterInterLinks.jl#readme"
625+
""")
626+
#!format: on
627+
readinv = Inventory(filename; root_url="")
628+
@test readinv.project == "Test"
629+
487630
end
488631

489632
end

0 commit comments

Comments
 (0)