This repository has been archived by the owner on Aug 24, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
MimeType.io
245 lines (215 loc) · 7.3 KB
/
MimeType.io
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// Bug in true/false JSON support. Let's patch it.
if(true hasSlot("asJson") not, true asJson := method(asString))
if(false hasSlot("asJson") not, false asJson := method(asString))
if(nil hasSlot("asJson") not, nil asJson := method("null"))
// The definition of one MIME content-type.
MimeType := Object clone do(
VERSION := method("1.0")
simplify := method(value,
if(MEDIA_TYPE_RE matchesIn(value asString) count isNil,
InvalidContentType(value)
)
value split("/") map(part,
part := part lowercase
if(part findSeq("x-") == 0, part := part replaceFirstSeq("x-", "") lowercase)
if(part findSeq("X-") == 0, part := part replaceFirstSeq("X-", "") lowercase)
part
) join("/")
)
# Builds a MimeType object from the provided MIME Content Type value—
# 'text/plain' or 'applicaton/x-eruby'. The constructed object is yielded to
# an optional block for additional configuration, such as associating
# extensions and encoding information.
#
# * When provided a Map or a MimeType, the MimeType will be constructed with
# #init_with.
# * When provided an Array, the MimeType will be constructed only using the
# first two elements of the array as the content type and extensions.
# * Otherwise, the content_type will be used as a string.
with := method(value,
if(value isKindOf(MimeType), value := MimeType asMap)
result := if(value isKindOf(Map),
self clone initWithMap(value)
,
if(value isKindOf(List),
self clone initWithList(value)
,
self clone initWithList(list(value))
)
)
result
)
isLike := method(other,
if(other hasSlot(simplified),
return(simplified == other simplified)
,
return(simplified == MimeType simplify(other))
)
)
isObsolete := method(self obsolete not not)
isRegistered := method(
if(self registered isNil,
partRegistered(self rawMediaType) and partRegistered(self rawSubType)
,
self registered not not
)
)
isBinary := method(BINARY_ENCODINGS contains(self encoding))
isAscii := method(isBinary not)
isSignature := method(signature not not)
isComplete := method(extensions isNotEmpty)
urls := method(
xrefs map(xrefType, values,
if(xrefType == "rfc",
values map(data, "http://www.iana.org/go/" .. data)
,
if(xrefType == "draft",
values map(data,
if(data findSeq("RFC") == 0, data replaceFirstSeq("RFC", "draft"))
"http://www.iana.org/go/" .. data
)
,
if(xrefType == "rfc-errata",
values map(data,
"http://www.rfc-editor.org/errata_search.php?eid=" .. data
)
,
if(xrefType == "person",
values map(data,
"http://www.iana.org/assignments/media-types/media-types.xhtml#" ..
data
)
,
if(xrefType == "template",
values map(data,
"http://www.iana.org/assignments/media-types/" .. data
)
,
if(xrefType == "uri" or xrefType == "text", values, nil)
)
)
)
)
)
) remove(nil) flatten
)
asString := method(self contentType)
asJson := method(asMap asJson)
asMap := method(
m := Map clone
m atPut("content-type", self contentType)
if(self docs isNotEmpty, m atPut("docs", self docs))
m atPut("encoding", self encoding)
if(self extensions isNotEmpty, m atPut("extensions", self extensions))
if(self isObsolete,
m atPut("obsolete", self isObsolete)
if(self useInstead isNotEmpty, m atPut("use-instead", self useInstead))
)
if(self xrefs isNotEmpty, m atPut("xrefs", self xrefs))
m atPut("registered", self isRegistered)
if(self isSignature, m atPut("signature", self isSignature))
m
)
partRegistered := method(value, value findSeq("x-") != 0)
compare := method(other,
if(other hasSlot(contentType),
return contentType lowercase compare(other contentType lowercase)
,
return simplified compare(MimeType simplify(other asString))
)
)
priorityCompare := method(other,
pc := simplified compare(other simplified)
if(pc != 0, return pc)
if(isRegistered != other isRegistered, return if(isRegistered, -1, 1))
if(isComplete != other isComplete, return(isComplete, -1, 1))
if(isObsolete != other isObsolete, return(isObsolete, 1, -1))
if(isObsolete and useInstead != otherUseInstead,
if(useInstead isNil, return 1)
if(other useInstead isNil, return -1)
return useInstead compare(other useInstead)
,
return 0
)
)
InvalidContentType := method(invalidType,
Exception raise("Invalid Content-Type #{invalidType}" interpolate)
)
InvalidEncoding := method(encoding,
Exception raise("Invalid Encoding #{encoding}" interpolate)
)
MEDIA_TYPE_RE := Regex with("([-\\w.+]+)/([-\\w.+]*)")
UNREGISTERED_RE := Regex with("[Xx]-")
DEFAULT_ENCODINGS := list(nil, "default")
BINARY_ENCODINGS := list("base64", "8bit")
TEXT_ENCODINGS := list("7bit", "quoted-printable")
VALID_ENCODINGS := list(DEFAULT_ENCODINGS, BINARY_ENCODINGS, TEXT_ENCODINGS) flatten
contentType := nil
rawMediaType := nil
rawSubType := nil
simplified := nil
mediaType := nil
subType := nil
obsolete := false
registered := nil
signature := nil
encoding := DEFAULT_ENCODINGS last
useInstead := List clone
extensions := List clone
docs := List clone
xrefs := Map clone
setContentType := method(value,
matches := MEDIA_TYPE_RE matchesIn(value)
if(matches count isNil,
InvalidContentType(value)
,
matches := matches last
)
self contentType := value
self rawMediaType := matches captures at(1)
self rawSubType := matches captures at(2)
self simplified := MimeType simplify(value)
matches := MEDIA_TYPE_RE matchesIn(self simplified) last
self mediaType := matches captures at(1)
self subType := matches captures at(2)
self contentType
)
setExtensions := method(values,
self extensions := list(values) flatten remove(nil) sortInPlace unique
)
addExtensions := method(values,
setExtensions(list(self extensions, values))
)
setEncoding := method(value,
if(DEFAULT_ENCODINGS contains(value),
self encoding := self defaultEncoding,
if(BINARY_ENCODINGS contains(value) or TEXT_ENCODINGS contains(value),
self encoding := value
,
InvalidEncoding(value)
)
)
)
defaultEncoding := method(
if(self mediaType == "text", TEXT_ENCODINGS last, BINARY_ENCODINGS first)
)
initWithMap := method(coder,
setContentType(coder at("content-type"))
if(coder hasKey("docs"), self docs := coder at("docs"))
setEncoding(coder at("encoding"))
setExtensions(coder at("extensions"))
self obsolete := coder at("obsolete")
self registered := coder at("registered")
self signature := coder at("signature")
if(coder hasKey("xrefs"), self xrefs := coder at("xrefs"))
if(coder hasKey("use-instead"), self use_instead := coder at("use-instead"))
self
)
initWithList := method(value,
setContentType(value at(0))
setExtensions(list(value slice(1)) flatten)
setEncoding
self
)
)
tp := MimeType with(list("text/plain", "txt", "md"))