Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve documentation regarding supported output formats #28

Merged
merged 12 commits into from
Jul 22, 2022
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ Enables a wide array of textual diagramming tools, such as
[Graphviz](https://www.graphviz.org), [Mermaid](https://mermaidjs.github.io),
[PlantUML](https://plantuml.com),
[svgbob](https://ivanceras.github.io/content/Svgbob.html) and [many
more](https://kroki.io/#support) within Julia through the
[Kroki](https://kroki.io) service. It works in environments capable of
rendering images:
more](https://bauglir.github.io/Kroki.jl/stable/#diagram-support) within Julia
through the [Kroki](https://kroki.io) service. It works in environments capable
of rendering images:

![Kroki Pluto Demo](./docs/src/kroki-demo-pluto.gif)

Expand Down
3 changes: 3 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ Modules = [ Kroki.StringLiterals ]
## Private

```@docs
DiagramTypeMetadata
DIAGRAM_TYPE_METADATA
LIMITED_DIAGRAM_SUPPORT
MIME_TO_RENDER_ARGUMENT_MAP
UriSafeBase64Payload
getDiagramTypeMetadata
```

### Documentation
Expand Down
4 changes: 2 additions & 2 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ on a [`Diagram`](@ref), specifying the desired output format.
!!! warning "Output format support"

All diagram types support SVG output, other supported output formats vary
per diagram type. See [Kroki's website](https://kroki.io/#support) for a,
not entirely accurate, overview.
per diagram type. See [the support table](@ref diagram-support) for an
overview.

```@example diagrams
mermaid_diagram = mermaid"""
Expand Down
25 changes: 22 additions & 3 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
Enables a wide array of textual diagramming tools, such as
[Graphviz](https://www.graphviz.org), [Mermaid](https://mermaidjs.github.io),
[PlantUML](https://plantuml.com),
[svgbob](https://ivanceras.github.io/content/Svgbob.html) and [many
more](https://kroki.io/#support) within Julia through the
[Kroki](https://kroki.io) service.
[svgbob](https://ivanceras.github.io/content/Svgbob.html) and [many more](@ref
diagram-support) within Julia through the [Kroki](https://kroki.io) service.

![Kroki REPL Demo](./kroki-demo-repl.gif)

Expand Down Expand Up @@ -63,6 +62,26 @@ Kroki.Service.start!), [`status`](@ref Kroki.Service.status), [`stop!`](@ref
Kroki.Service.stop!), etc. are included to help with the self-hosting scenario,
provided [Docker Compose](https://docs.docker.com/compose) is available.

## [Supported Diagram Types](@id diagram-support)

The table below provides an overview of the different diagram types this
package supports, with links to their documentation, and the output formats
they can be rendered to.

```@eval
using Kroki, Markdown
Markdown.parse(Kroki.renderDiagramSupportAsMarkdown(Kroki.LIMITED_DIAGRAM_SUPPORT))
```

The information in this table should correspond to the one on [Kroki's
website](https://kroki.io/#support), but is directly derived from the support
as it is encoded in [this
package](https://bauglir.github.io/Kroki.jl/stable/api/#Kroki.LIMITED_DIAGRAM_SUPPORT).
Given that this information is a mirror of the information available on
[Kroki's website](https://kroki.io/#support), it may not be entirely accurate
with regards to actually supported output formats. Support for output formats
needs to be addressed within the Kroki service.

## Contents

```@contents
Expand Down
133 changes: 122 additions & 11 deletions src/Kroki.jl
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ a known type of error occurs. Other errors (e.g.
`HTTP.ExceptionRequest.StatusError` for connection errors) are propagated if
they occur.

_SVG output is supported for all [`Diagram`](@ref) types_. See [Kroki's
website](https://kroki.io/#support) for an overview of other supported output
formats per diagram type. Note that this list may not be entirely up-to-date.
_SVG output is supported for all [`Diagram`](@ref) types_. See the [support
table](@ref diagram-support) for an overview of other supported output formats
per diagram type.
"""
render(
diagram::Diagram,
Expand Down Expand Up @@ -175,12 +175,121 @@ render(
throw(RenderError(diagram, exception))
end

# Helper constant to type a map from MIME to `Diagram` types more easily
const MIMEToDiagramTypeMap = Dict{MIME, Tuple{Symbol, Vararg{Symbol}}}

# Helper function to render a support matrix for inclusion in Markdown
# documentation, e.g. `LIMITED_DIAGRAM_SUPPORT`'s docstring
function renderDiagramSupportAsMarkdown(support::MIMEToDiagramTypeMap)
# SVG support should always be included for all diagram types, even if it is
# not in the support map
mime_types = MIME.(sort(unique([string.(keys(support))..., "image/svg+xml"])))

header = """
| | `$(join(mime_types, "` | `"))` |
| --: $(repeat("| :-: ", length(mime_types)))|
"""

diagram_types = sort(unique(Iterators.flatten(values(support))))
diagram_types_with_support = map(diagram_types) do diagram_type
diagram_type_metadata = getDiagramTypeMetadata(diagram_type)

return [
"[$(diagram_type_metadata.name)]($(diagram_type_metadata.url))",
map(
mime -> mime === MIME"image/svg+xml"() || diagram_type ∈ support[mime] ? "✅" : "",
mime_types,
)...,
]
end

diagram_support_markdown_rows =
string.("| ", join.(diagram_types_with_support, " | "), " |")

return header * join(diagram_support_markdown_rows, '\n')
end

"""
A container to associate metadata with a specific diagram type, e.g. for
documentation purposes.
"""
struct DiagramTypeMetadata
"A more readable name for the diagram type, if applicable."
name::String

"The URL to the website/documentation of the diagram type."
url::String
end

"""
An overview of metadata for the different [`Diagram`](@ref) `type`s that can be
rendered, e.g. links to the main documentation, friendly names, etc.
"""
DIAGRAM_TYPE_METADATA = Dict{Symbol, DiagramTypeMetadata}(
:actdiag => DiagramTypeMetadata("actdiag", "http://blockdiag.com/en/actdiag"),
:blockdiag => DiagramTypeMetadata("blockdiag", "http://blockdiag.com/en/blockdiag"),
:bpmn => DiagramTypeMetadata("BPMN", "https://www.omg.org/spec/BPMN"),
:bytefield =>
DiagramTypeMetadata("Byte Field", "https://bytefield-svg.deepsymmetry.org"),
:c4plantuml => DiagramTypeMetadata(
"C4 with PlantUML",
"https://github.com/plantuml-stdlib/C4-PlantUML",
),
:diagramsnet => DiagramTypeMetadata("diagrams.net", "https://diagrams.net"),
:ditaa => DiagramTypeMetadata("ditaa", "http://ditaa.sourceforge.net"),
:erd => DiagramTypeMetadata("erd", "https://github.com/BurntSushi/erd"),
:excalidraw => DiagramTypeMetadata("Excalidraw", "https://excalidraw.com"),
:graphviz => DiagramTypeMetadata("Graphviz", "https://graphviz.org"),
:mermaid => DiagramTypeMetadata("Mermaid", "https://mermaid-js.github.io"),
:nomnoml => DiagramTypeMetadata("nomnoml", "https://www.nomnoml.com"),
:nwdiag => DiagramTypeMetadata("nwdiag", "http://blockdiag.com/en/nwdiag"),
:packetdiag => DiagramTypeMetadata("packetdiag", "http://blockdiag.com/en/nwdiag"),
:pikchr => DiagramTypeMetadata("Pikchr", "https://pikchr.org"),
:plantuml => DiagramTypeMetadata("PlantUML", "https://plantuml.com"),
:rackdiag => DiagramTypeMetadata("rackdiag", "http://blockdiag.com/en/nwdiag"),
:seqdiag => DiagramTypeMetadata("seqdiag", "http://blockdiag.com/en/seqdiag"),
:structurizr => DiagramTypeMetadata("Structurizr", "https://structurizr.com"),
:svgbob =>
DiagramTypeMetadata("Svgbob", "https://ivanceras.github.io/content/Svgbob.html"),
:umlet => DiagramTypeMetadata("UMLet", "https://github.com/umlet/umlet"),
:vega => DiagramTypeMetadata("Vega", "https://vega.github.io/vega"),
:vegalite => DiagramTypeMetadata("Vega-Lite", "https://vega.github.io/vega-lite"),
:wavedrom => DiagramTypeMetadata("WaveDrom", "https://wavedrom.com"),
)

"""
Retrieves the metadata for a given `diagram_type` from
[`DIAGRAM_TYPE_METADATA`](@ref), with a fallback to a generic
[`DiagramTypeMetadata`](@ref).
"""
getDiagramTypeMetadata(diagram_type::Symbol)::DiagramTypeMetadata = get(
DIAGRAM_TYPE_METADATA,
diagram_type,
DiagramTypeMetadata(
"$(diagram_type)",
"https://bauglir.github.io/Kroki.jl/stable/#diagram-support",
),
)

"""
Some MIME types are not supported by all diagram types, this constant contains
all these limitations. The union of all values corresponds to all supported
[`Diagram`](@ref) `type`s.

Note that SVG output is supported by all diagram types, as is reflected in the
support matrix below. Only those diagram types that explicitly _only_ support
SVG output are included in this constant.

Those diagram types that support plain text output, i.e. not just rendering
their `specification`, support both ASCII and Unicode character sets for
rendering. This is expressed using two different `text/plain` MIME types. See
also [`SUPPORTED_TEXT_PLAIN_SHOW_MIME_TYPES`](@ref).

# Support Matrix

$(renderDiagramSupportAsMarkdown(LIMITED_DIAGRAM_SUPPORT))
"""
const LIMITED_DIAGRAM_SUPPORT = Dict{MIME, Tuple{Symbol, Vararg{Symbol}}}(
const LIMITED_DIAGRAM_SUPPORT = MIMEToDiagramTypeMap(
MIME"application/pdf"() => (
:blockdiag,
:seqdiag,
Expand Down Expand Up @@ -213,12 +322,8 @@ const LIMITED_DIAGRAM_SUPPORT = Dict{MIME, Tuple{Symbol, Vararg{Symbol}}}(
:vega,
:vegalite,
),
# Although all diagram types support SVG, these _only_ support SVG so are
# included separately
MIME"image/svg+xml"() =>
(:bpmn, :bytefield, :excalidraw, :nomnoml, :pikchr, :svgbob, :wavedrom),
# Diagrams that can be rendered to plain text support both ASCII and Unicode
# rendering
MIME"text/plain"() => (:c4plantuml, :plantuml, :structurizr),
MIME"text/plain; charset=utf-8"() => (:c4plantuml, :plantuml, :structurizr),
)
Expand Down Expand Up @@ -276,14 +381,20 @@ for the `text/plain` MIME type.

Should be set to a variation of the `text/plain` MIME type. For instance,
`text/plain; charset=utf-8` to enable Unicode rendering for certain diagrams,
e.g. PlantUML and Structurizr. Only a select number of variations are
supported, see [`LIMITED_DIAGRAM_SUPPORT`](@ref) for details.
e.g. PlantUML and Structurizr.

Only a select number of variations are supported, see
[`LIMITED_DIAGRAM_SUPPORT`](@ref) and
[`SUPPORTED_TEXT_PLAIN_SHOW_MIME_TYPES`](@ref) for details.

Defaults to `$(TEXT_PLAIN_SHOW_MIME_TYPE[])`.
"""
const TEXT_PLAIN_SHOW_MIME_TYPE = Ref{MIME}(MIME"text/plain; charset=utf-8"())

"All values that can be used to configure [`TEXT_PLAIN_SHOW_MIME_TYPE`](@ref)."
"""
The values that can be used to configure [`TEXT_PLAIN_SHOW_MIME_TYPE`](@ref):
$(join(string.(" * `", SUPPORTED_TEXT_PLAIN_SHOW_MIME_TYPES), "`\n"))`
"""
const SUPPORTED_TEXT_PLAIN_SHOW_MIME_TYPES = Set([
mime for
mime in keys(Kroki.LIMITED_DIAGRAM_SUPPORT) if startswith(string(mime), "text/plain")
Expand Down
35 changes: 3 additions & 32 deletions src/kroki/string_literals.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module StringLiterals

using ..Kroki: Diagram, LIMITED_DIAGRAM_SUPPORT
using ..Kroki: Diagram, LIMITED_DIAGRAM_SUPPORT, getDiagramTypeMetadata

# Helper function implementing string interpolation to be used in conjunction
# with macros defining diagram specification string literals, as they do not
Expand Down Expand Up @@ -68,35 +68,6 @@ function shouldInterpolate(stream::IO)
return iseven(n_escape_characters)
end

# Links to the main documentation for each diagram type for inclusion in the
# string literal docstrings
DIAGRAM_DOCUMENTATION_URLS = Dict{Symbol, String}(
:actdiag => "http://blockdiag.com/en/actdiag",
:blockdiag => "http://blockdiag.com/en/blockdiag",
:bpmn => "https://www.omg.org/spec/BPMN",
:bytefield => "https://bytefield-svg.deepsymmetry.org",
:c4plantuml => "https://github.com/plantuml-stdlib/C4-PlantUML",
:diagramsnet => "https://diagrams.net",
:ditaa => "http://ditaa.sourceforge.net",
:erd => "https://github.com/BurntSushi/erd",
:excalidraw => "https://excalidraw.com",
:graphviz => "https://graphviz.org",
:mermaid => "https://mermaid-js.github.io",
:nomnoml => "https://www.nomnoml.com",
:nwdiag => "http://blockdiag.com/en/nwdiag",
:packetdiag => "http://blockdiag.com/en/nwdiag",
:pikchr => "https://pikchr.org",
:plantuml => "https://plantuml.com",
:rackdiag => "http://blockdiag.com/en/nwdiag",
:seqdiag => "http://blockdiag.com/en/seqdiag",
:structurizr => "https://structurizr.com",
:svgbob => "https://ivanceras.github.io/content/Svgbob.html",
:umlet => "https://github.com/umlet/umlet",
:vega => "https://vega.github.io/vega",
:vegalite => "https://vega.github.io/vega-lite",
:wavedrom => "https://wavedrom.com",
)

# The union of the values of `LIMITED_DIAGRAM_SUPPORT` corresponds to all
# supported `Diagram` types. The `values` call returns an array of arrays that
# may contain duplicate diagram types due to some types supporting rendering to
Expand All @@ -109,9 +80,9 @@ for diagram_type in unique(Iterators.flatten(values(LIMITED_DIAGRAM_SUPPORT)))
# variable. First for `@eval`, then for the macro itself
macro_diagram_type = QuoteNode(QuoteNode(diagram_type))

diagram_url = get(DIAGRAM_DOCUMENTATION_URLS, diagram_type, "https://kroki.io/#support")
diagram_type_metadata = getDiagramTypeMetadata(diagram_type)

docstring = "String literal for instantiating [`$diagram_type`]($diagram_url) [`Diagram`](@ref)s."
docstring = "String literal for instantiating [`$(diagram_type_metadata.name)`]($(diagram_type_metadata.url)) [`Diagram`](@ref)s."

@eval begin
export $macro_signature
Expand Down
80 changes: 80 additions & 0 deletions test/kroki/diagram_instantiation_test.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
module DiagramInstantiationTest

using Test: @test, @testset

using Kroki: Diagram

@testset "`Diagram` instantiation" begin
@testset "through keyword arguments" begin
expected_specification = "[...]--[...]"
expected_type = :svgbob

diagram = Diagram(specification = expected_specification, type = expected_type)

@test diagram.specification === expected_specification
@test diagram.type === expected_type

@testset "optionally accepts `options`" begin
expected_options = Dict("key" => "value")

diagram = Diagram(
options = expected_options,
specification = expected_specification,
type = expected_type,
)

@test diagram.options === expected_options
@test diagram.specification === expected_specification
@test diagram.type === expected_type
end
end

@testset "through `type` and `specification` positional arguments" begin
expected_specification = "A -> B: C"
expected_type = :plantuml

diagram = Diagram(expected_type, expected_specification)

@test diagram.specification === expected_specification
@test diagram.type === expected_type

@testset "optionally accepts `options`" begin
expected_options = Dict("foo" => "bar")

diagram = Diagram(expected_type, expected_specification; options = expected_options)

@test diagram.options === expected_options
@test diagram.specification === expected_specification
@test diagram.type === expected_type
end
end

@testset "through `type` positional argument with keyword arguments" begin
@testset "providing the `path` keyword argument loads a file as the `specification" begin
diagram_path = joinpath(@__DIR__, "..", "assets", "plantuml-example.puml")
expected_specification = read(diagram_path, String)

diagram = Diagram(:plantuml; path = diagram_path)

@test diagram.specification === expected_specification
end

@testset "optionally accepts `options`" begin
expected_options = Dict("key" => "value")

diagram = Diagram(:plantuml; options = expected_options, specification = "A -> B: C")

@test diagram.options === expected_options
end

@testset "providing the `specification` keyword argument stores it" begin
expected_specification = "A -> B: C"

diagram = Diagram(:plantuml; specification = expected_specification)

@test diagram.specification === expected_specification
end
end
end

end
Loading