Files
soleprint/soleprint/atlas2/docgen/emitters/auto.py

64 lines
2.5 KiB
Python

"""
Draw it the way its structure asks to be drawn.
`ops.classify` reads the structure and names an emitter; this runs it. The whole
point is that nobody should have to know that a schema wants cards and a module
graph wants ranks — or discover it from a 235:1 image.
When the answer is "this is not a diagram", it says so and produces the index,
because that *is* the right artifact for a flat list of peers.
## One function, three callers
This branch — erd, or index, or dot through Graphviz — used to be written out
three times: in the `auto` command, in the `site` command choosing its picture,
and in the book choosing its graph step. Three copies of "which drawing does this
graph want" is three chances for them to disagree about the answer, so it lives
here and returns content rather than writing it. Where the file goes, and what
to call it, is the caller's business.
"""
from ..ops import classify
# What each emitter produces, so a caller can name the file without knowing
# which emitter ran.
SUFFIX = {"erd": ".svg", "dot": ".svg", "index": ".md"}
def draw(ir: dict, style, *, force: str | None = None) -> dict:
"""The drawing this graph asks for, as content.
Returns::
{"verdict": <classify's verdict>,
"emitter": "erd" | "dot" | "index",
"suffix": ".svg" | ".md",
"content": str | bytes | None, # None when it could not be drawn
"why": why this emitter, or why nothing was drawn}
Never raises for a missing Graphviz. A layered graph on a machine without
`dot` is a normal state — the book records the step as skipped and the site
goes text-only — so it is reported in `why` with `content` None.
"""
verdict = classify(ir)
chosen = force or verdict["emitter"]
out = {"verdict": verdict, "emitter": chosen, "suffix": SUFFIX.get(chosen, ".svg"),
"content": None, "why": verdict["why"]}
if chosen == "erd":
from .erd import emit
out["content"] = emit(ir, style)
elif chosen == "index":
from .index import to_markdown
out["content"] = to_markdown(ir)
elif chosen == "dot":
from .dot import emit, have_graphviz, render
if not have_graphviz():
out["why"] = "Graphviz is not installed — nothing drawn (apt install graphviz)"
else:
opts = verdict.get("options") or {}
out["content"] = render(emit(ir, style, rankdir=opts.get("rankdir")))
else:
raise ValueError(f"no emitter {chosen!r} — have: erd, dot, index")
return out