-
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy patherrors.typ
More file actions
210 lines (190 loc) · 6.45 KB
/
Copy patherrors.typ
File metadata and controls
210 lines (190 loc) · 6.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Unified error reporting for src/.
//
// Typst cannot catch a panic, so the message *builders* are pure functions
// returning strings (tests/unit/test-errors.typ asserts their wording) and the
// `fail-*` / `check` wrappers raise them via `panic` / `assert`.
//
// Message grammar:
// "<scope>: <problem>; got <repr(value)>. <hint>"
// - scope: the public function or module ("compose", "geom-curve").
// - problem: the expectation, without a trailing period.
// - got: appended only when a value is shown; always via repr().
// - one trailing full stop.
// - hint: optional final sentence giving the fix.
//
// Never inline a panic string elsewhere in src/; route every validation here.
// Render an array of values as a quoted, comma-joined list: "a", "b", "c".
#let quote-each(values) = values.map(v => "\"" + str(v) + "\"").join(", ")
// A cm figure in a layout message, rounded to the sub-millimetre the reader can
// act on. Every layout failure quotes room the same way, so the rounding lives
// here rather than beside each `fail`.
#let cm-text(value) = str(calc.round(value, digits: 2))
// Append a hint sentence when one is supplied.
#let _with-hint(text, hint) = if hint == none { text } else {
text + " " + hint
}
// "<scope>: <problem>." plus optional hint. `problem` may already carry its own
// "; got ..." clause for bespoke messages.
#let error-text(scope, problem, hint: none) = {
_with-hint(scope + ": " + problem + ".", hint)
}
// "<scope>: <name> must be one of "a", "b"; got <repr(value)>."
#let enum-text(scope, name, value, valid, hint: none) = {
error-text(
scope,
name + " must be one of " + quote-each(valid) + "; got " + repr(value),
hint: hint,
)
}
// "<scope>: <name> must be <expected>; got <repr(value)>."
// `expected` is a noun phrase, e.g. "a positive integer", "a length or `auto`".
#let type-text(scope, name, value, expected, hint: none) = {
error-text(
scope,
name + " must be " + expected + "; got " + repr(value),
hint: hint,
)
}
// "<scope>: <name> must be in (lo, hi); got <repr(value)>."
// lo-open/hi-open pick the bracket: open -> "(" / ")", closed -> "[" / "]".
#let range-text(
scope,
name,
value,
lo,
hi,
lo-open: true,
hi-open: true,
hint: none,
) = {
let open = if lo-open { "(" } else { "[" }
let close = if hi-open { ")" } else { "]" }
let interval = open + repr(lo) + ", " + repr(hi) + close
error-text(
scope,
name + " must be in " + interval + "; got " + repr(value),
hint: hint,
)
}
// "<scope>: <label> maps to unknown column <repr(col)>; available columns:
// "a", "b"." `label` is the aesthetic name for a mapping, or the facet role
// ("variable", "rows", "columns") for a facet.
#let unknown-column-text(scope, label, col, available, hint: none) = {
error-text(
scope,
label
+ " maps to unknown column "
+ repr(col)
+ "; available columns: "
+ quote-each(available),
hint: hint,
)
}
// Panic / assert wrappers ----------------------------------------------------
#let fail(scope, problem, hint: none) = {
panic(error-text(scope, problem, hint: hint))
}
#let fail-enum(scope, name, value, valid, hint: none) = {
panic(enum-text(scope, name, value, valid, hint: hint))
}
#let fail-type(scope, name, value, expected, hint: none) = {
panic(type-text(scope, name, value, expected, hint: hint))
}
#let fail-range(
scope,
name,
value,
lo,
hi,
lo-open: true,
hi-open: true,
hint: none,
) = {
panic(range-text(
scope,
name,
value,
lo,
hi,
lo-open: lo-open,
hi-open: hi-open,
hint: hint,
))
}
#let check(cond, scope, problem, hint: none) = {
assert(cond, message: error-text(scope, problem, hint: hint))
}
#let fail-unknown-column(scope, label, col, available, hint: none) = {
panic(unknown-column-text(scope, label, col, available, hint: hint))
}
// Panic unless `align` is a Typst alignment (the house type for every text /
// legend alignment). `none` is allowed: it means "inherit the default". Guards
// against passing a string like "left" (which silently never matches the
// alignment comparisons downstream). `name` names the field in the message,
// for a caller that takes more than one alignment.
#let assert-halign(scope, align, name: "align") = {
if align != none and type(align) != alignment {
fail-type(
scope,
name,
align,
"a Typst alignment `left`, `center`, or `right`",
hint: "Use the alignment value `left`, not the string \"left\".",
)
}
}
// Panic unless `value` is a Typst length or `none` (inherit the theme
// default). Guards against passing a raw number or string where a glyph
// diameter is expected.
#let assert-length(scope, name, value) = {
if value != none and type(value) != length {
fail-type(
scope,
name,
value,
"a length such as `0.3cm`, or `none`",
hint: "Use `0.3cm` for an absolute size; `none` keeps the theme default.",
)
}
}
// Panic unless `size` is an absolute length, a ratio (a `%` value relative to
// the parent surface size), or `none` (inherit). Guards against passing a raw
// number or string where a typed size is expected.
#let assert-text-size(scope, size) = {
if size != none and type(size) != length and type(size) != ratio {
fail-type(
scope,
"size",
size,
"a length (e.g., `12pt`), a ratio (e.g., `80%`), or `none`",
hint: "Use `12pt` for an absolute size or `80%` to scale the parent size.",
)
}
}
// Panic unless `stroke` is an absolute length, a ratio (a `%` value relative to
// the parent surface stroke), or `none` (inherit). Guards against passing a raw
// number or string where a typed stroke thickness is expected.
#let assert-stroke(scope, stroke) = {
if stroke != none and type(stroke) != length and type(stroke) != ratio {
fail-type(
scope,
"stroke",
stroke,
"a length (e.g., `1pt`), a ratio (e.g., `80%`), or `none`",
hint: "Use `1pt` for an absolute thickness or `80%` to scale the parent stroke.",
)
}
}
// Panic unless `length` is an absolute length, a ratio (a `%` value relative to
// the parent surface tick length), or `none` (inherit).
#let assert-tick-length(scope, value) = {
if value != none and type(value) != length and type(value) != ratio {
fail-type(
scope,
"length",
value,
"a length (e.g., `0.1cm`), a ratio (e.g., `50%`), or `none`",
hint: "Use `0.1cm` for an absolute tick length or `50%` to scale the parent length.",
)
}
}