nitdoc: set icons for each kind of cards
[nit.git] / src / web / web_base.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Base classes used by `nitweb`.
16 module web_base
17
18 import model::model_views
19 import model::model_json
20 import doc_down
21 import popcorn
22 import popcorn::pop_config
23 import popcorn::pop_repos
24 import popcorn::pop_json
25
26 # Nitweb config file.
27 class NitwebConfig
28 super AppConfig
29
30 redef fun default_db_name do return "nitweb"
31
32 # Model to use.
33 var model: Model
34
35 # MModule used to flatten model.
36 var mainmodule: MModule
37
38 # Modelbuilder used to access sources.
39 var modelbuilder: ModelBuilder
40
41 # The JSON API does not filter anything by default.
42 #
43 # So we can cache the model view.
44 var view: ModelView is lazy do
45 var view = new ModelView(model)
46 view.min_visibility = private_visibility
47 view.include_fictive = true
48 view.include_empty_doc = true
49 view.include_attribute = true
50 view.include_test_suite = true
51 return view
52 end
53 end
54
55 # Specific handler for the nitweb API.
56 abstract class APIHandler
57 super Handler
58
59 # App config.
60 var config: NitwebConfig
61
62 # Find the MEntity ` with `full_name`.
63 fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
64 if full_name == null then return null
65 return model.mentity_by_full_name(full_name.from_percent_encoding)
66 end
67
68 # Try to load the mentity from uri with `/:id`.
69 #
70 # Send 400 if `:id` is null.
71 # Send 404 if no entity is found.
72 # Return null in both cases.
73 fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
74 var id = req.param("id")
75 if id == null then
76 res.api_error(400, "Expected mentity full name")
77 return null
78 end
79 var mentity = find_mentity(config.view, id)
80 if mentity == null then
81 res.api_error(404, "MEntity `{id}` not found")
82 end
83 return mentity
84 end
85 end
86
87 # A Rooter dedicated to APIHandlers.
88 class APIRouter
89 super Router
90
91 # App config
92 var config: NitwebConfig
93 end
94
95 redef class HttpResponse
96
97 # Return an HTTP error response with `status`
98 #
99 # Like the rest of the API, errors are formated as JSON:
100 # ~~~json
101 # { "status": 404, "message": "Not found" }
102 # ~~~
103 fun api_error(status: Int, message: String) do
104 json(new APIError(status, message), status)
105 end
106
107 # Write data as JSON and set the right content type header.
108 fun raw_json(json: nullable String, status: nullable Int) do
109 header["Content-Type"] = media_types["json"].as(not null)
110 if json == null then
111 send(null, status)
112 else
113 send(json, status)
114 end
115 end
116 end
117
118 # An error returned by the API.
119 #
120 # Can be serialized to json.
121 class APIError
122 serialize
123
124 # Reponse status
125 var status: Int
126
127 # Response error message
128 var message: String
129 end
130
131 # Fullname representation that can be used to build decorated links
132 #
133 # * MPackage: `mpackage_name`
134 # * MGroup: `(mpackage_name::)mgroup_name`
135 class Namespace
136 super Array[nullable NSEntity]
137 super NSEntity
138 serialize
139
140 redef fun to_s do return self.join("")
141 redef fun serialize_to(v) do to_a.serialize_to(v)
142 end
143
144 # Something that goes in a Namespace
145 #
146 # Can be either:
147 # * a `NSToken` for tokens like `::`, `>` and `$`
148 # * a `MSRef` for references to mentities
149 interface NSEntity
150 super Serializable
151 end
152
153 # A reference to a MEntity that can be rendered as a link.
154 #
155 # We do not reuse `MEntityRef` ref since NSRef can be found in a ref and create
156 # an infinite loop.
157 class NSRef
158 super NSEntity
159 serialize
160
161 # The mentity to link to/
162 var mentity: MEntity
163
164 redef fun core_serialize_to(v) do
165 v.serialize_attribute("web_url", mentity.web_url)
166 v.serialize_attribute("api_url", mentity.api_url)
167 v.serialize_attribute("name", mentity.name)
168 end
169 end
170
171 # A namespace token representation
172 #
173 # Used for namespace tokens like `::`, `>` and `$`
174 redef class String
175 super NSEntity
176 end
177
178 redef class MEntity
179
180 # URL to `self` within the web interface.
181 fun web_url: String do return "/doc/" / full_name
182
183 # URL to `self` within the JSON api.
184 fun api_url: String do return "/api/entity/" / full_name
185
186 redef fun core_serialize_to(v) do
187 super
188 v.serialize_attribute("namespace", namespace)
189 v.serialize_attribute("web_url", web_url)
190 v.serialize_attribute("api_url", api_url)
191 end
192
193 # Return `self.full_name` as an object that can be serialized to json.
194 fun namespace: nullable Namespace do return null
195
196 # Return a new NSRef to `self`.
197 fun to_ns_ref: NSRef do return new NSRef(self)
198 end
199
200 redef class MEntityRef
201 redef fun core_serialize_to(v) do
202 super
203 v.serialize_attribute("namespace", mentity.namespace)
204 v.serialize_attribute("web_url", mentity.web_url)
205 v.serialize_attribute("api_url", mentity.api_url)
206 v.serialize_attribute("name", mentity.name)
207 v.serialize_attribute("mdoc", mentity.mdoc_or_fallback)
208 v.serialize_attribute("visibility", mentity.visibility.to_s)
209 v.serialize_attribute("modifiers", mentity.collect_modifiers)
210 v.serialize_attribute("class_name", mentity.class_name)
211 var mentity = self.mentity
212 if mentity isa MMethod then
213 v.serialize_attribute("msignature", mentity.intro.msignature)
214 else if mentity isa MMethodDef then
215 v.serialize_attribute("msignature", mentity.msignature)
216 else if mentity isa MVirtualTypeProp then
217 v.serialize_attribute("bound", to_mentity_ref(mentity.intro.bound))
218 else if mentity isa MVirtualTypeDef then
219 v.serialize_attribute("bound", to_mentity_ref(mentity.bound))
220 end
221 v.serialize_attribute("location", mentity.location)
222 end
223 end
224
225 redef class MDoc
226
227 # Add doc down processing
228 redef fun core_serialize_to(v) do
229 v.serialize_attribute("html_synopsis", html_synopsis.write_to_string)
230 end
231 end
232
233 redef class MPackage
234 redef fun namespace do return new Namespace.from([to_ns_ref])
235 end
236
237 redef class MGroup
238 redef fun namespace do
239 var p = parent
240 if p == null then
241 return new Namespace.from([to_ns_ref, ">": nullable NSEntity])
242 end
243 return new Namespace.from([p.namespace, to_ns_ref, ">": nullable NSEntity])
244 end
245 end
246
247 redef class MModule
248 redef fun namespace do
249 var mgroup = self.mgroup
250 if mgroup == null then
251 return new Namespace.from([to_ns_ref])
252 end
253 return new Namespace.from([mgroup.mpackage.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
254 end
255
256 private fun ns_for(visibility: MVisibility): nullable Namespace do
257 if visibility <= private_visibility then return namespace
258 var mgroup = self.mgroup
259 if mgroup == null then return namespace
260 return mgroup.mpackage.namespace
261 end
262 end
263
264 redef class MClass
265 redef fun namespace do
266 return new Namespace.from([intro_mmodule.ns_for(visibility), "::", to_ns_ref: nullable NSEntity])
267 end
268 end
269
270 redef class MClassDef
271 redef fun namespace do
272 if is_intro then
273 return new Namespace.from([mmodule.ns_for(mclass.visibility), "$", to_ns_ref: nullable NSEntity])
274 else if mclass.intro_mmodule.mpackage != mmodule.mpackage then
275 return new Namespace.from([mmodule.namespace, "$", mclass.namespace: nullable NSEntity])
276 else if mclass.visibility > private_visibility then
277 return new Namespace.from([mmodule.namespace, "$", mclass.to_ns_ref: nullable NSEntity])
278 end
279 return new Namespace.from([mmodule.full_name, "$::", mclass.intro_mmodule.to_ns_ref: nullable NSEntity])
280 end
281
282 redef fun web_url do return "{mclass.web_url}/lin#{full_name}"
283 end
284
285 redef class MProperty
286 redef fun namespace do
287 if intro_mclassdef.is_intro then
288 return new Namespace.from([intro_mclassdef.mmodule.ns_for(visibility), "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
289 else
290 return new Namespace.from([intro_mclassdef.mmodule.namespace, "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
291 end
292 end
293 end
294
295 redef class MPropDef
296 redef fun namespace do
297 var res = new Namespace
298 res.add mclassdef.namespace
299 res.add "$"
300
301 if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
302 res.add to_ns_ref
303 else
304 if mclassdef.mmodule.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then
305 res.add mproperty.intro_mclassdef.mmodule.ns_for(mproperty.visibility)
306 res.add "::"
307 else if mproperty.visibility <= private_visibility then
308 if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mpackage then
309 res.add "::"
310 res.add mproperty.intro_mclassdef.mmodule.to_ns_ref
311 res.add "::"
312 end
313 end
314 if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
315 res.add mproperty.intro_mclassdef.to_ns_ref
316 res.add "::"
317 end
318 res.add to_ns_ref
319 end
320 return res
321 end
322
323 redef fun web_url do return "{mproperty.web_url}/lin#{full_name}"
324 end
325
326 redef class MClassType
327 redef var web_url = mclass.web_url is lazy
328 end
329
330 redef class MNullableType
331 redef var web_url = mtype.web_url is lazy
332 end
333
334 redef class MParameterType
335 redef var web_url = mclass.web_url is lazy
336 end
337
338 redef class MVirtualType
339 redef var web_url = mproperty.web_url is lazy
340 end
341
342 redef class POSetElement[E]
343 super Serializable
344
345 redef fun core_serialize_to(v) do
346 assert self isa POSetElement[MEntity]
347 v.serialize_attribute("direct_greaters", to_mentity_refs(direct_greaters))
348 v.serialize_attribute("direct_smallers", to_mentity_refs(direct_smallers))
349 end
350 end