1 /**
2 	Serves documentation on through HTTP server.
3 
4 	Copyright: © 2012 RejectedSoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module ddox.htmlserver;
9 
10 import ddox.api;
11 import ddox.ddoc; // just so that rdmd picks it up
12 import ddox.entities;
13 import ddox.htmlgenerator;
14 import ddox.settings;
15 
16 import std.array;
17 import std..string;
18 import vibe.core.log;
19 import vibe.http.fileserver;
20 import vibe.http.router;
21 import vibe.templ.diet; // just so that rdmd picks it up
22 
23 
24 void registerApiDocs(URLRouter router, Package pack, GeneratorSettings settings = null)
25 {
26 	if( !settings ) settings = new GeneratorSettings;
27 
28 	string linkTo(Entity ent, size_t level)
29 	{
30 		auto dst = appender!string();
31 
32 		if( level ) foreach( i; 0 .. level ) dst.put("../");
33 		else dst.put("./");
34 
35 		if( ent !is null && ent.parent !is null ){
36 			auto dp = cast(VariableDeclaration)ent;
37 			auto dfn = cast(FunctionDeclaration)ent.parent;
38 			if( dp && dfn ) ent = ent.parent;
39 
40 			Entity[] nodes;
41 			size_t mod_idx = 0;
42 			while( ent ){
43 				if( cast(Module)ent ) mod_idx = nodes.length;
44 				nodes ~= ent;
45 				ent = ent.parent;
46 			}
47 			foreach_reverse(i, n; nodes[mod_idx .. $-1]){
48 				dst.put(n.name);
49 				if( i > 0 ) dst.put('.');
50 			}
51 			dst.put("/");
52 			foreach_reverse(i, n; nodes[0 .. mod_idx]){
53 				dst.put(n.name);
54 				if( i > 0 ) dst.put('.');
55 			}
56 
57 			if( dp && dfn ){
58 				dst.put('#');
59 				dst.put(dp.name);
60 			}
61 		}
62 
63 		return dst.data();
64 	}
65 
66 	void showApi(HTTPServerRequest req, HTTPServerResponse res)
67 	{
68 		res.contentType = "text/html; charset=UTF-8";
69 		generateApiIndex(res.bodyWriter, pack, settings, ent => linkTo(ent, 0), req);
70 	}
71 
72 	void showApiModule(HTTPServerRequest req, HTTPServerResponse res)
73 	{
74 		auto mod = pack.lookup!Module(req.params["modulename"]);
75 		if( !mod ) return;
76 
77 		res.contentType = "text/html; charset=UTF-8";
78 		generateModulePage(res.bodyWriter, pack, mod, settings, ent => linkTo(ent, 1), req);
79 	}
80 
81 	void showApiItem(HTTPServerRequest req, HTTPServerResponse res)
82 	{
83 		auto mod = pack.lookup!Module(req.params["modulename"]);
84 		logDebug("mod: %s", mod !is null);
85 		if( !mod ) return;
86 		auto item = mod.lookup!Declaration(req.params["itemname"], false);
87 		logDebug("item: %s", item !is null);
88 		if( !item ) return;
89 
90 		res.contentType = "text/html; charset=UTF-8";
91 		generateDeclPage(res.bodyWriter, pack, mod, item, settings, ent => linkTo(ent, 1), req);
92 	}
93 
94 	void showSitemap(HTTPServerRequest req, HTTPServerResponse res)
95 	{
96 		res.contentType = "application/xml";
97 		generateSitemap(res.bodyWriter, pack, settings, ent => linkTo(ent, 0), req);
98 	}
99 
100 	void showSymbolJS(HTTPServerRequest req, HTTPServerResponse res)
101 	{
102 		res.contentType = "application/javascript";
103 		generateSymbolsJS(res.bodyWriter, pack, settings, ent => linkTo(ent, 0));
104 	}
105 
106 	auto path_prefix = settings.siteUrl.path.toString();
107 	if( path_prefix.endsWith("/") ) path_prefix = path_prefix[0 .. $-1];
108 
109 	router.get(path_prefix~"/", &showApi);
110 	router.get(path_prefix~"/:modulename/", &showApiModule);
111 	router.get(path_prefix~"/:modulename/:itemname", &showApiItem);
112 	router.get(path_prefix~"/sitemap.xml", &showSitemap);
113 	router.get(path_prefix~"/symbols.js", &showSymbolJS);
114 	router.get("*", serveStaticFiles("public"));
115 
116 	// convenience redirects (when leaving off the trailing slash)
117 	if( path_prefix.length ) router.get(path_prefix, staticRedirect(path_prefix~"/"));
118 	router.get(path_prefix~"/:modulename", delegate(req, res){ res.redirect(path_prefix~"/"~req.params["modulename"]~"/"); });
119 }