""" 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": , "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