src/pub/blurb.bend
ezx@1.8.0.0 · 124 lines · 0 definitions
1# pub/blurb: the line the hub shows as a package's description, pure. The
2# hub takes the first line of the package's first file by path, in plain
3# string order (so an uppercase letter comes before every lowercase one), and
4# passes over a file named LICENSE, which bend 2.0.27 sends beside the files
5# it publishes. `ez publish` says this line before it uploads (pub/plan.bend),
6# and `ez init` opens the entry it writes with a line of its own for it
7# (init/plan.bend), so the line a project is shown by on the hub is one its
8# author wrote.
9#
10# Paths are compared with `String.is_le`, the order the package walk writes a
11# manifest in (`K.file.le`). It compares characters by code point, which is
12# JavaScript's order for every path whose characters are all in the Basic
13# Multilingual Plane.
14import Base
15import ezx@1.8.0.0/src/pkg/pkg.bend as K
16import ezx@1.8.0.0/src/pkg/path.bend as Path
17
18# one step of a scan for a newline, with the head's answer in hand; the rest
19# arrives as a thunk, so the first newline ends the scan
20def nl.step(here: Bool, rest: Unit -> Bool) -> Bool:
21 match here:
22 case True{}:
23 True{}
24 case False{}:
25 rest(Unit{})
26
27# whether a text holds a newline, so is more than one line
28def has.nl(text: String) -> Bool:
29 match text:
30 case SNil{}:
31 False{}
32 case SCon{+h, t}:
33 nl.step(Char.is_eq(h, '\n'), _u => has.nl(t))
34
35# one step of the first line: a newline ends it, and any other character
36# starts what the rest of the line is
37def line.step(end: Bool, +head: Char, rest: Unit -> String) -> String:
38 match end:
39 case True{}:
40 SNil{}
41 case False{}:
42 SCon{head, rest(Unit{})}
43
44# a text's first line: everything before its first newline, the whole text
45# when it has none
46def line.first(text: String) -> String:
47 match text:
48 case SNil{}:
49 SNil{}
50 case SCon{+h, t}:
51 line.step(Char.is_eq(h, '\n'), h, _u => line.first(t))
52
53# whether a file of a package is a LICENSE, which the hub passes over
54def lic(file: K.Source) -> Bool:
55 K.Source{at, _text} = file
56 String.eq(Path.base(at), "LICENSE")
57
58# where a file stands in the hub's order
59def at(file: K.Source) -> String:
60 K.Source{a, _text} = file
61 a
62
63# the earlier of the file found so far and the next one; a tie keeps the one
64# found first
65def least.vs(cur: Maybe<&2, K.Source>, +next: K.Source) -> Maybe<&2, K.Source>:
66 match cur:
67 case None{}:
68 Some{next}
69 case Some{+c}:
70 Bool.pick(Maybe<&2, K.Source>, String.is_le(at(c), at(next)), Some{c}, Some{next})
71
72# the next file weighed: a LICENSE is passed over, and any other file is the
73# earlier of itself and the one found so far
74def least.pick(skip: Bool, cur: Maybe<&2, K.Source>, +next: K.Source) -> Maybe<&2, K.Source>:
75 match skip:
76 case True{}:
77 cur
78 case False{}:
79 least.vs(cur, next)
80
81# the first file by path among these and the one found so far, LICENSE files
82# passed over
83def least(fs: List<&2, K.Source>, cur: Maybe<&2, K.Source>) -> Maybe<&2, K.Source>:
84 match fs:
85 case []:
86 cur
87 case +h <> t:
88 least(t, least.pick(lic(h), cur, h))
89
90# a file's first line
91def line.file(file: K.Source) -> String:
92 K.Source{_at, text} = file
93 line.first(text)
94
95# the first line of the file found, "" when there is none
96def line.of(got: Maybe<&2, K.Source>) -> String:
97 match got:
98 case None{}:
99 ""
100 case Some{f}:
101 line.file(f)
102
103# the line the hub describes a package by: the first line of its first file
104# by path that is not a LICENSE
105def blurb(fs: List<&2, K.Source>) -> String:
106 line.of(least(fs, None{}))
107
108# one step of the walk below, with the head's answer in hand; the rest
109# arrives as a thunk, so the first file that comes earlier ends the walk
110def after.step(here: Bool, rest: Unit -> Bool) -> Bool:
111 match here:
112 case True{}:
113 rest(Unit{})
114 case False{}:
115 False{}
116
117# whether every file of these comes no earlier than a path, a LICENSE
118# included. Law vocabulary: a file whose path is this one comes first.
119def after(+path: String, fs: List<&2, K.Source>) -> Bool:
120 match fs:
121 case []:
122 True{}
123 case +h <> t:
124 after.step(String.is_le(path, at(h)), _u => after(path, t))