Skip to content

Commit 9a65de4

Browse files
committed
wip: Add valkyrie validation
1 parent 447df61 commit 9a65de4

11 files changed

+341
-89
lines changed

src/_pkg.typ

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
#import "@preview/hydra:0.4.0"
33
#import "@preview/showybox:2.0.1"
44
#import "@preview/t4t:0.3.2"
5+
#import "@local/valkyrie:0.2.0" as z

src/_valid.typ

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#import "_pkg.typ"
2+
3+
#let _content = content
4+
#let _color = color
5+
#let _gradient = gradient
6+
#let _version = version
7+
8+
#import _pkg.z: *
9+
10+
#let paint = base-type.with(name: "color/gradient", types: (_color, _gradient,))
11+
12+
#let auto_ = base-type.with(name: "auto", types: (type(auto),))

src/author.typ

+41-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#import "/src/_pkg.typ"
2+
#import "/src/_valid.typ"
3+
14
/// The marks used in `author` for automatic mark numbering.
25
///
36
/// Can be used to add new or alter existing marks, marks come in the form of
@@ -9,14 +12,26 @@
912

1013
/// Display a person with the given options.
1114
///
12-
/// - ..first (str): The first and middle names.
13-
/// - last (str, array): The last names.
1415
/// - short (int): The shortness level.
1516
/// - `0`: do not shorten any names
1617
/// - `1`: shorten middle names
1718
/// - `2`: shorten middle and first names
19+
/// - ..first (str): The first and middle names.
20+
/// - last (str, array): The last names.
1821
/// -> content
19-
#let name(..first, last, short: 1) = {
22+
#let name(
23+
short: 1,
24+
..first,
25+
last,
26+
_validate: true,
27+
) = {
28+
if _validate {
29+
import _valid as z
30+
_ = z.parse(first, z.sink(positional: z.array(z.string())), scope: ("first",))
31+
_ = z.parse(last, z.array(z.string(), pre-transform: z.coerce.array), scope: ("last",))
32+
_ = z.parse(short, z.integer(min: 0, max: 2), scope: ("short",))
33+
}
34+
2035
first = first.pos()
2136

2237
if type(first) == str {
@@ -52,23 +67,33 @@
5267

5368
/// Show full author information, see also `name`.
5469
///
55-
/// - ..first (str, none): The first and middle names.
56-
/// - last (str, array): The last names.
5770
/// - short (int): The shortness level.
5871
/// - `0`: do not shorten any names
5972
/// - `1`: shorten middle names
6073
/// - `2`: shorten middle and first names
6174
/// - label (label, none): The label to use for email attribution, see `email`.
6275
/// - email (str, content, none): The email to attribute the user to.
76+
/// - ..first (str): The first and middle names.
77+
/// - last (str, array): The last names.
6378
/// -> content
6479
#let author(
65-
..first,
66-
last,
6780
short: 1,
6881
label: none,
6982
email: none,
83+
..first,
84+
last,
85+
_validate: true,
7086
) = {
71-
name(..first, last, short: short)
87+
if _validate {
88+
import _valid as z
89+
_ = z.parse(first, z.sink(positional: z.array(z.string())), scope: ("first",))
90+
_ = z.parse(last, z.array(z.string(), pre-transform: z.coerce.array), scope: ("last",))
91+
_ = z.parse(short, z.integer(min: 0, max: 2), scope: ("short",))
92+
_ = z.parse(email, z.content(optional: true), scope: ("email",))
93+
_ = z.parse(label, z.label(optional: true), scope: ("label",))
94+
}
95+
96+
name(..first, last, short: short, _validate: false)
7297

7398
if label != none {
7499
context super(marks.final().marks.at(str(label)))
@@ -94,7 +119,15 @@
94119
mark: auto,
95120
label,
96121
body,
122+
_validate: true,
97123
) = {
124+
if _validate {
125+
import _valid as z
126+
_ = z.parse(body, z.content(optional: true), scope: ("body",))
127+
_ = z.parse(mark, z.either(z.content(optional: true), z.auto_()), scope: ("mark",))
128+
_ = z.parse(label, z.label(optional: true), scope: ("label",))
129+
}
130+
98131
if mark == auto {
99132
marks.update(m => {
100133
m.current-auto += 1;

src/component/table-of-contents.typ

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#import "/src/_pkg.typ"
2+
#import "/src/_valid.typ"
23
#import "/src/theme.typ" as _theme
34

45
#let _columns = columns
56

6-
#let fill(arr, len, new) = if arr.len() >= len {
7+
#let _fill(arr, len, new) = if arr.len() >= len {
78
arr
89
} else {
910
arr + ((new,) * (len - arr.len()))
@@ -24,11 +25,17 @@
2425
target: heading.where(outlined: true),
2526
columns: 1,
2627
theme: _theme.default,
28+
_validate: true,
2729
) = {
28-
_pkg.t4t.assert.any-type(str, content, title)
29-
_pkg.t4t.assert.any-type(selector, function, target)
30-
_pkg.t4t.assert.any-type(int, columns)
31-
_pkg.t4t.assert.any-type(dictionary, theme)
30+
if _validate {
31+
import _valid as z
32+
33+
_ = z.parse(title, z.content(), scope: ("title",))
34+
_ = z.parse(columns, z.integer(), scope: ("columns",))
35+
_ = z.parse(theme, _theme.schema(), scope: ("theme",))
36+
37+
_pkg.t4t.assert.any-type(selector, function, target)
38+
}
3239

3340
// NOTE: unsure if this looks good, this also doens't work in CI for now
3441
// set text(font: theme.fonts.headings)
@@ -61,7 +68,7 @@
6168

6269
for part in parts {
6370
if part.level >= indent-stack.len() {
64-
indent-stack = fill(indent-stack, part.level, 0em)
71+
indent-stack = _fill(indent-stack, part.level, 0em)
6572
}
6673

6774
let level-max = indent-stack.at(part.level - 1)

src/component/title-page.typ

+29-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "/src/_pkg.typ"
2+
#import "/src/_valid.typ"
23
#import "/src/theme.typ" as _theme
34

45
#let _version = version
@@ -28,25 +29,38 @@
2829
abstract: lorem(100),
2930
license: "MIT",
3031
theme: _theme.default,
32+
_validate: true,
3133
) = {
32-
let assert-text = _pkg.t4t.assert.any-type.with(str, content)
33-
let assert-maybe-text = _pkg.t4t.assert.any-type.with(str, content, type(none))
34+
if _validate {
35+
import _valid as z
3436

35-
assert-text(title)
36-
assert-maybe-text(subtitle)
37-
assert-text(array, authors)
38-
_pkg.t4t.assert.any-type(str, array, type(none), urls)
39-
assert-maybe-text(abstract)
40-
_pkg.t4t.assert.any-type(_version, version)
41-
assert-maybe-text(license)
42-
_pkg.t4t.assert.any-type(dictionary, theme)
37+
_ = z.parse(title, z.content(), scope: ("title",))
38+
_ = z.parse(subtitle, z.content(optional: true), scope: ("subtitle",))
39+
_ = z.parse(abstract, z.content(optional: true), scope: ("abstract",))
40+
_ = z.parse(license, z.content(optional: true), scope: ("license",))
41+
_ = z.parse(theme, _theme.schema(), scope: ("theme",))
4342

44-
if authors != none {
45-
authors = _pkg.t4t.def.as-arr(authors)
46-
}
43+
authors = z.parse(
44+
authors,
45+
z.array(z.string(), min: 1, pre-transform: z.coerce.array),
46+
scope: ("authors",),
47+
)
48+
49+
urls = z.parse(
50+
urls,
51+
z.array(
52+
z.string(),
53+
pre-transform: z.coerce.array,
54+
post-transform: (self, it) => if it == (none,) { none } else { it },
55+
optional: true,
56+
),
57+
scope: ("urls",),
58+
)
4759

48-
if urls != none {
49-
urls = _pkg.t4t.def.as-arr(urls)
60+
_pkg.t4t.assert.any-type(_version, version)
61+
} else {
62+
authors = if type(authors) == str { (authors,) } else { authors }
63+
urls = if type(urls) == str { (urls,) } else { urls }
5064
}
5165

5266
pad(y: 10em, x: 5em, {

src/example.typ

+34-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "/src/_pkg.typ"
2+
#import "/src/_valid.typ"
23
#import "/src/theme.typ" as _theme
34

45
/// Show a source code frame.
@@ -8,37 +9,57 @@
89
/// -> content
910
#let frame(
1011
theme: _theme.default,
12+
_validate: true,
1113
..args,
12-
) = _pkg.showybox.showybox(
13-
frame: (
14-
border-color: theme.colors.primary,
15-
title-color: theme.colors.primary,
16-
thickness: .75pt,
17-
radius: 4pt,
18-
inset: 8pt
19-
),
20-
..args
21-
)
14+
) = {
15+
if _validate {
16+
import _valid as z
17+
_ = z.parse(theme, _theme.schema(), scope: ("theme",))
18+
// NOTE: intentionally left empty, we only validte our own inputs
19+
}
20+
21+
_pkg.showybox.showybox(
22+
frame: (
23+
border-color: theme.colors.primary,
24+
title-color: theme.colors.primary,
25+
thickness: .75pt,
26+
radius: 4pt,
27+
inset: 8pt
28+
),
29+
..args
30+
)
31+
}
2232

2333
/// Shows example code and its corresponding output in a frame.
2434
///
2535
/// - side-by-side (bool): Whether or not the example source and output should
2636
/// be shown side by side.
2737
/// - scope (dictionary): The scope to pass to `eval`.
2838
/// - breakable (bool): If the frame can brake over multiple pages.
29-
/// - theme (theme): The theme to use for this code example.
3039
/// - result (content): The content to render as the example result. Evaluated
3140
/// `eval`.
3241
/// - source (content): A raw element containing the source code to evaluate.
42+
/// - theme (theme): The theme to use for this code example.
3343
/// -> content
3444
#let code-result(
3545
side-by-side: false,
3646
scope: (:),
3747
breakable: false,
38-
theme: _theme.default,
3948
result: auto,
4049
source,
50+
theme: _theme.default,
51+
_validate: true,
4152
) = {
53+
if _validate {
54+
import _valid as z
55+
_ = z.parse(side-by-side, z.boolean(), scope: ("side-by-side",))
56+
_ = z.parse(breakable, z.boolean(), scope: ("breakable",))
57+
_ = z.parse(scope, z.dictionary((:)), scope: ("scope",))
58+
_ = z.parse(result, z.either(z.content(), z.auto_()), scope: ("result",))
59+
_ = z.parse(source, z.content(), scope: ("source",))
60+
_ = z.parse(theme, _theme.schema(), scope: ("theme",))
61+
}
62+
4263
let mode = if source.lang == "typc" {
4364
"code"
4465
} else if source.lang in ("typ", "typst") {
@@ -62,6 +83,6 @@
6283
grid.hline(stroke: 0.75pt + theme.colors.primary)
6384
},
6485
result,
65-
)
86+
),
6687
)
6788
}

src/lib.typ

+32-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "_pkg.typ"
2+
#import "_valid.typ"
23

34
#import "author.typ"
45
#import "component.typ"
@@ -36,31 +37,42 @@
3637
abstract: lorem(100),
3738
license: "MIT",
3839
theme: theme.default,
40+
_validate: true,
3941
) = body => {
40-
let assert-text = _pkg.t4t.assert.any-type.with(str, content)
41-
let assert-maybe-text = _pkg.t4t.assert.any-type.with(str, content, type(none))
42+
if _validate {
43+
import _valid as z
4244

43-
assert-text(title)
44-
assert-maybe-text(subtitle)
45-
assert-text(array, authors)
46-
_pkg.t4t.assert.any-type(str, array, type(none), urls)
47-
assert-maybe-text(abstract)
48-
_pkg.t4t.assert.any-type(_version, version)
49-
assert-maybe-text(license)
50-
_pkg.t4t.assert.any-type(dictionary, theme)
45+
z.parse(title, z.content(), scope: ("title",))
46+
z.parse(subtitle, z.content(optional: true), scope: ("subtitle",))
47+
z.parse(abstract, z.content(optional: true), scope: ("abstract",))
48+
z.parse(license, z.content(optional: true), scope: ("license",))
49+
z.parse(theme, _theme.schema, scope: ("theme",))
5150

52-
let authors = authors
53-
if authors != none {
54-
authors = _pkg.t4t.def.as-arr(authors)
55-
}
51+
authors = z.parse(
52+
z.array(z.string(), min: 1, pre-transform: z.coerce.array),
53+
authors,
54+
scope: ("authors",),
55+
)
56+
57+
urls = z.parse(
58+
urls,
59+
z.array(
60+
z.string(),
61+
pre-transform: z.coerce.array,
62+
post-transform: (self, it) => if it == (none,) { none } else { it },
63+
optional: true,
64+
),
65+
scope: ("urls",),
66+
)
5667

57-
let urls = urls
58-
if urls != none {
59-
urls = _pkg.t4t.def.as-arr(urls)
68+
_pkg.t4t.assert.any-type(_version, version)
69+
} else {
70+
authors = if type(authors) == str { (authors,) } else { authors }
71+
urls = if type(urls) == str { (urls,) } else { urls }
6072
}
6173

6274
set document(title: title, author: authors)
63-
show: style.default(theme: theme)
75+
show: style.default(theme: theme, _validate: false)
6476

6577
component.make-title-page(
6678
title: title,
@@ -72,12 +84,14 @@
7284
abstract: abstract,
7385
license: license,
7486
theme: theme,
87+
_validate: false,
7588
)
7689

7790
component.make-table-of-contents(
7891
title: heading(outlined: false, numbering: none, level: 2)[Table of Contents],
7992
columns: 1,
8093
theme: theme,
94+
_validate: false,
8195
)
8296

8397
body

0 commit comments

Comments
 (0)