-
Notifications
You must be signed in to change notification settings - Fork 7
/
ascii.libsonnet
132 lines (112 loc) · 4.06 KB
/
ascii.libsonnet
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
local d = import 'github.com/jsonnet-libs/docsonnet/doc-util/main.libsonnet';
{
'#': d.pkg(
name='ascii',
url='github.com/jsonnet-libs/xtd/ascii.libsonnet',
help='`ascii` implements helper functions for ascii characters',
),
local cp(c) = std.codepoint(c),
'#isLower':: d.fn(
'`isLower` reports whether ASCII character `c` is a lower case letter',
[d.arg('c', d.T.string)]
),
isLower(c): cp(c) >= 97 && cp(c) < 123,
'#isUpper':: d.fn(
'`isUpper` reports whether ASCII character `c` is a upper case letter',
[d.arg('c', d.T.string)]
),
isUpper(c): cp(c) >= 65 && cp(c) < 91,
'#isNumber':: d.fn(
'`isNumber` reports whether character `c` is a number.',
[d.arg('c', d.T.string)]
),
isNumber(c): std.isNumber(c) || (cp(c) >= 48 && cp(c) < 58),
'#isStringNumeric':: d.fn(
'`isStringNumeric` reports whether string `s` consists only of numeric characters.',
[d.arg('str', d.T.string)]
),
isStringNumeric(str): std.all(std.map(self.isNumber, std.stringChars(str))),
'#isStringJSONNumeric':: d.fn(
'`isStringJSONNumeric` reports whether string `s` is a number as defined by [JSON](https://www.json.org/json-en.html).',
[d.arg('str', d.T.string)]
),
isStringJSONNumeric(str):
// "1" "9"
local onenine(c) = (cp(c) >= 49 && cp(c) <= 57);
// "0"
local digit(c) = (cp(c) == 48 || onenine(c));
local digits(str) =
std.length(str) > 0
&& std.all(
std.foldl(
function(acc, c)
acc + [digit(c)],
std.stringChars(str),
[],
)
);
local fraction(str) = str == '' || (str[0] == '.' && digits(str[1:]));
local sign(c) = (c == '-' || c == '+');
local exponent(str) =
str == ''
|| (str[0] == 'E' && digits(str[1:]))
|| (str[0] == 'e' && digits(str[1:]))
|| (std.length(str) > 1 && str[0] == 'E' && sign(str[1]) && digits(str[2:]))
|| (std.length(str) > 1 && str[0] == 'e' && sign(str[1]) && digits(str[2:]));
local integer(str) =
(std.length(str) == 1 && digit(str[0]))
|| (std.length(str) > 0 && onenine(str[0]) && digits(str[1:]))
|| (std.length(str) > 1 && str[0] == '-' && digit(str[1]))
|| (std.length(str) > 1 && str[0] == '-' && onenine(str[1]) && digits(str[2:]));
local expectInteger =
if std.member(str, '.')
then std.split(str, '.')[0]
else if std.member(str, 'e')
then std.split(str, 'e')[0]
else if std.member(str, 'E')
then std.split(str, 'E')[0]
else str;
local expectFraction =
if std.member(str, 'e')
then std.split(str[std.length(expectInteger):], 'e')[0]
else if std.member(str, 'E')
then std.split(str[std.length(expectInteger):], 'E')[0]
else str[std.length(expectInteger):];
local expectExponent = str[std.length(expectInteger) + std.length(expectFraction):];
std.all([
integer(expectInteger),
fraction(expectFraction),
exponent(expectExponent),
]),
'#stringToRFC1123': d.fn(
|||
`stringToRFC113` converts a strings to match RFC1123, replacing non-alphanumeric characters with dashes. It'll throw an assertion if the string is too long.
* RFC 1123. This means the string must:
* - contain at most 63 characters
* - contain only lowercase alphanumeric characters or '-'
* - start with an alphanumeric character
* - end with an alphanumeric character
|||,
[d.arg('str', d.T.string)]
),
stringToRFC1123(str):
// lowercase alphabetic characters
local lowercase = std.asciiLower(str);
// replace non-alphanumeric characters with dashes
local alphanumeric =
std.join(
'',
std.map(
function(c)
if self.isLower(c)
|| self.isNumber(c)
then c
else '-',
std.stringChars(lowercase)
)
);
// remove leading/trailing dashes
local return = std.stripChars(alphanumeric, '-');
assert std.length(return) <= 63 : 'String too long';
return,
}