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 }