pub fn cmd_explain(code: &str) -> u8 {
let code = code.to_uppercase();
match REGISTRY.iter().find(|(c, _)| *c == code) {
Some((c, text)) => {
println!("{c}: {}", text.trim());
0
}
None => {
eprintln!("error: unknown code `{code}`");
let known: Vec<&str> = REGISTRY.iter().map(|(c, _)| *c).collect();
eprintln!("known codes: {}", known.join(", "));
2
}
}
}
const REGISTRY: &[(&str, &str)] = &[
(
"E0101",
r#"
Unexpected character.
The lexer hit a character that isn't part of drawlang syntax. Common causes:
smart quotes pasted from a document (use plain "double quotes"), tabs inside
identifiers, or stray punctuation. Strings take double quotes only.
"#,
),
(
"E0102",
r#"
Unterminated or malformed string.
Strings open and close with `"` on one line. For multi-line labels embed
`\n`: label: "line one\nline two". Valid escapes: \" \\ \n \t \{ \}.
"#,
),
(
"E0103",
r#"
Expected one token, found another.
The parser knows exactly what it needs here and tells you what it found
instead. The label under the caret names the expectation. Most often a
missing `:` in a property, a missing `{`, or a missing comma in a list.
"#,
),
(
"E0104",
r#"
Broken string interpolation.
`{expr}` inside a string evaluates the expression: "GPU {i}". This error
means a `{` never closed, the braces were empty, or more than one expression
was inside. For a literal brace, escape it: \{.
"#,
),
(
"E0105",
r#"
Malformed number, color, or grid size.
Numbers are plain pixels with no unit suffix (write `width: 200`, not
`200px`). Colors are `#rgb`, `#rrggbb`, or `#rrggbbaa`. Grid sizes are
`COLSxROWS`, like `grid 2x4`.
"#,
),
(
"E0106",
r#"
Unsupported drawl version.
The header pins the language version this file was written against. This
tool reads version 0.1: `drawl 0.1`.
"#,
),
(
"E0107",
r#"
Declaration names must be single identifiers.
`gpus[0]` or `host.cpu` *refer* to elements; declarations create them, so
they take a plain name: `cpu { ... }`. To nest, declare inside the parent's
block.
"#,
),
(
"E0108",
r#"
`[*]` outside a constraint.
The wildcard expands to all children of a container and only makes sense in
constraint arguments, like `align(gpus[*], top)`. Edges and properties need
a specific element: `gpus[0]`.
"#,
),
(
"E0110",
r#"
Not a statement.
Statements are: nodes (`id { ... }`), containers (`id: row { ... }`), edges
(`a -> b`), properties (`key: value`), or the keywords canvas, def, group,
class, constrain, pin, for, port. Check for a typo in the first word — the
error suggests the closest keyword when one is plausible.
"#,
),
(
"W0101",
r#"
Missing version header.
Files should start with `drawl 0.1` so future versions of the tool can keep
old diagrams rendering identically.
"#,
),
(
"W0102",
r#"
Duplicate canvas block.
Only one `canvas { ... }` block applies; the later one wins. Merge them.
"#,
),
(
"E0201",
r#"
Unknown element.
The path's first segment is looked up in the current scope, then in each
enclosing scope (lexical scoping). Statements at the top level see only
top-level names — to reach into a group, qualify the path: `host.cpu`, not
`cpu`. The error lists visible names and suggests the closest one.
"#,
),
(
"E0202",
r#"
Index out of range.
`container[i]` indexes children in declaration order, starting at 0. The
error reports how many children exist. Loop bounds are a common off-by-one
source: `for i in 0..4` runs i = 0, 1, 2, 3.
"#,
),
(
"E0203",
r#"
Duplicate name.
Sibling elements, ports on one node, and top-level defs/classes each share a
namespace; two declarations with one name make paths ambiguous. Rename one,
or use a `for` loop index in the name's label instead.
"#,
),
(
"E0204",
r#"
Unknown component.
`name(args)` instantiates `def name(params) { ... }`. Defs live at the top
level. The error suggests the closest defined component.
"#,
),
(
"E0205",
r#"
Wrong number of component arguments.
The instantiation must match the def's parameter list exactly. The
diagnostic shows the definition site and its parameters.
"#,
),
(
"E0206",
r#"
Unknown variable.
Variables come from `for` loop counters and component parameters, and are
visible only inside their block. The error lists what's in scope.
"#,
),
(
"E0207",
r#"
Wildcard must end the path.
`gpus[*]` is the whole set of children; nothing can come after it.
"#,
),
(
"E0208",
r#"
Self-edge.
An edge needs two distinct endpoints. To show a loopback, model it as two
ports on the node and connect those.
"#,
),
(
"E0209",
r#"
Edge into own container.
An element can't connect to something that contains it — there's no
meaningful place for the arrow to start and end. Connect two siblings, or a
specific child of the container.
"#,
),
(
"E0301",
r#"
Unknown property.
Properties are validated against the known set per context. The diagnostic
suggests the closest key or lists them all. See `drawlang cheatsheet` for
the full table.
"#,
),
(
"E0302",
r#"
Wrong value type for this property.
Each property takes one shape of value: `width: 200` (number), `label: "x"`
(string), `dashed: true` (boolean word), `fill: @accent` (color). The error
shows the expected form.
"#,
),
(
"E0303",
r#"
Unknown keyword value.
This property takes one of a fixed set of words (e.g. `side: top|right|
bottom|left`). The error suggests the closest valid one.
"#,
),
(
"E0304",
r#"
Unknown theme token.
Tokens resolve against the active theme so one source renders well in both:
@accent @accent2 @hot @ok @warn @muted @faint @ink @bg @surface. Prefer
tokens over raw hex.
"#,
),
(
"E0305",
r#"
Unknown class.
`class: name` references a top-level `class name { ... }`. Classes must be
defined in the same file. The error suggests the closest defined class.
"#,
),
(
"E0306",
r#"
Bad loop bound.
`for i in a..b` needs whole numbers with b > a, and runs at most 4096
iterations. The range is half-open: `0..4` is 0, 1, 2, 3.
"#,
),
(
"E0307",
r#"
Division or modulo by zero in an expression.
"#,
),
(
"E0308",
r#"
Type mismatch in an expression.
Arithmetic needs numbers. `+` also concatenates when either side is a
string. Note `%` is Euclidean (always non-negative) so ring indices like
`(i + 1) % n` behave.
"#,
),
(
"E0309",
r#"
Statement in the wrong place.
canvas/def/class live at the top level only; ports live inside nodes;
classes and canvas blocks contain only properties. The message says which
rule applies.
"#,
),
(
"E0310",
r#"
Grid overfilled.
`grid 2x4` holds 8 children (columns x rows). Enlarge the grid or remove
children; extra rows are not invented silently.
"#,
),
(
"E0311",
r#"
Too many elements (limit 4096).
Almost always a runaway `for` loop or unintended nesting. The limit keeps
renders fast and reports readable.
"#,
),
(
"E0312",
r#"
Component instantiation too deep (limit 64).
A `def` is instantiating itself, directly or through another component.
Diagrams are finite; break the cycle, or build repetition with `for` loops
instead of recursion.
"#,
),
(
"E0401",
r#"
Unknown constraint.
Constraints: align(a, b, ..., EDGE), gap(a, b, PX), below/above/left-of/
right-of(a, b), same-width/same-height/same-size(a, b, ...).
"#,
),
(
"E0402",
r#"
Wrong constraint arguments.
The help line shows the exact usage. Targets are element paths (wildcard
`container[*]` allowed for align/same-*); distances are plain pixel numbers.
"#,
),
(
"E0403",
r#"
Bad alignment edge.
The last argument of align() picks the edge: top, bottom, left, right,
centerx, centery.
"#,
),
(
"E0404",
r#"
Constraint targets aren't siblings.
Constraints act within one container's coordinate system. To relate elements
in different containers, constrain the containers themselves.
"#,
),
(
"E0405",
r#"
Pin on a nested element.
Pins set absolute canvas positions and only apply to top-level elements.
Pin the outermost container; layout places its contents.
"#,
),
(
"E0406",
r#"
Conflicting constraints.
No layout can satisfy this constraint together with the earlier ones (e.g.
gap(a, b, 50) and gap(a, b, 100)). Remove or relax one. The first
conflicting constraint is the one flagged.
"#,
),
(
"W0301",
r#"
Label overflows its shape.
Only possible when an explicit width/height is smaller than the measured
text. Fixes, best first: remove the explicit size (boxes auto-fit), set the
suggested larger width, or set `wrap: N` to wrap the text at N pixels.
"#,
),
(
"W0401",
r#"
Two elements overlap.
Usually pinned elements colliding, or conflicting constraints resolving on
top of each other. The lint names both elements and the overlap area; the
geometry report has exact rectangles.
"#,
),
(
"W0402",
r#"
Element outside the canvas.
The canvas normally grows to fit; this fires when pins push content past the
origin. Pins are content-relative pixels with (0, 0) at the top-left.
"#,
),
(
"W0410",
r#"
Edge cuts through a box.
Spline edges take the direct path and don't dodge obstacles. Set
`routing: orthogonal` on the edge or its class — orthogonal routes go around
boxes (that's also what buses and lanes should look like).
"#,
),
(
"W0411",
r#"
Edge label collides with something.
The label placer tries above/below each clear run of the route; this fires
when nothing clear was found. Shorten the label, or give the layout more
room (gap() on the crowded elements).
"#,
),
(
"W0412",
r#"
Heavily crossed edge.
This edge crosses three or more others — a strong signal the element order
fights the connection pattern. Reorder the declarations (layout follows
declaration order within a layer), or route the edge orthogonally.
"#,
),
(
"W0501",
r#"
Unused definition.
A def that's never instantiated or a class that's never referenced. Delete
it, or wire it up — dead definitions confuse future readers (and agents).
"#,
),
(
"W0502",
r#"
Constraint with no effect.
align/same-* with fewer than two targets after wildcard expansion. Often an
empty container or a wildcard on the wrong path.
"#,
),
(
"W0503",
r#"
Constraint vs. rigid container.
row/column/grid place children themselves. Inside them, align() picks the
cross-axis alignment, gap(a, b, px) adjusts the spacing between adjacent
children — but ordering constraints (below/left-of/...) do nothing: reorder
the declarations instead.
"#,
),
];