1 /** 2 * Copyright © DiamondMVC 2018 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.views.viewparser; 7 8 import diamond.core.apptype; 9 10 static if (!isWebApi) 11 { 12 import diamond.views.viewformats; 13 import diamond.templates; 14 15 import std..string : strip, format; 16 import std.array : replace, split; 17 import std.conv : to; 18 19 /** 20 * Parses the view parts into a view class. 21 * Params: 22 * allParts = All the parsed parts of the view template. 23 * viewName = The name of the view. 24 * route = The route of the view. (This is null if no route is specified or if using stand-alone) 25 * Returns: 26 * A string equivalent to the generated view class. 27 */ 28 string parseViewParts(Part[][string] allParts, string viewName, out string route) 29 { 30 route = null; 31 32 string viewClassMembersGeneration = ""; 33 string viewConstructorGeneration = ""; 34 string viewModelGenerateGeneration = ""; 35 string viewCodeGeneration = ""; 36 string viewPlaceHolderGeneration = ""; 37 bool hasController; 38 bool useBaseView; 39 bool hasDefaultSection; 40 41 foreach (sectionName,parts; allParts) 42 { 43 if (sectionName && sectionName.length) 44 { 45 viewCodeGeneration ~= "case \"" ~ sectionName ~ "\": 46 { 47 "; 48 } 49 else 50 { 51 hasDefaultSection = true; 52 viewCodeGeneration ~= "default: 53 { 54 "; 55 } 56 57 foreach (part; parts) 58 { 59 if (!part.content || !part.content.strip().length) 60 { 61 continue; 62 } 63 64 import diamond.extensions; 65 mixin ExtensionEmit!(ExtensionType.partParser, q{ 66 {{extensionEntry}}.parsePart( 67 part, 68 viewName, 69 viewClassMembersGeneration, viewConstructorGeneration, 70 viewModelGenerateGeneration, 71 viewCodeGeneration 72 ); 73 }); 74 emitExtension(); 75 76 switch (part.contentMode) 77 { 78 case ContentMode.appendContent: 79 { 80 viewCodeGeneration ~= parseAppendContent(part); 81 break; 82 } 83 84 case ContentMode.appendContentPlaceHolder: 85 { 86 viewCodeGeneration ~= parseAppendPlaceholderContent(part); 87 break; 88 } 89 90 case ContentMode.mixinContent: 91 { 92 viewCodeGeneration ~= part.content; 93 break; 94 } 95 96 case ContentMode.metaContent: 97 { 98 parseMetaContent( 99 part, 100 viewName, 101 viewClassMembersGeneration, viewConstructorGeneration, 102 viewModelGenerateGeneration, viewPlaceHolderGeneration, 103 useBaseView, 104 hasController, 105 route 106 ); 107 break; 108 } 109 110 default : break; 111 } 112 } 113 114 viewCodeGeneration ~= "break; 115 } 116 "; 117 } 118 119 if (!hasDefaultSection) { 120 viewCodeGeneration ~= "default: break;"; 121 } 122 123 static if (isWebServer) 124 { 125 return viewClassFormat.format( 126 viewName, 127 viewClassMembersGeneration, 128 viewConstructorGeneration, 129 viewModelGenerateGeneration, 130 hasController ? controllerHandleFormat : "", 131 viewPlaceHolderGeneration, 132 viewCodeGeneration, 133 endFormat 134 ); 135 } 136 else 137 { 138 return viewClassFormat.format( 139 viewName, 140 viewClassMembersGeneration, 141 viewConstructorGeneration, 142 viewModelGenerateGeneration, 143 viewPlaceHolderGeneration, 144 viewCodeGeneration, 145 endFormat 146 ); 147 } 148 } 149 150 private: 151 /** 152 * Parses content that can be appended as a place holder. 153 * Params: 154 * part = The part to parse. 155 * Returns: 156 * The appended result. 157 */ 158 string parseAppendPlaceholderContent(Part part) 159 { 160 return appendFormat.format("getPlaceHolder(`" ~ part.content ~ "`)"); 161 } 162 163 /** 164 * Parses content that can be appended. 165 * Params: 166 * part = The part to parse. 167 * Returns: 168 * The appended result. 169 */ 170 string parseAppendContent(Part part) 171 { 172 switch (part.name) 173 { 174 case "expressionValue": 175 { 176 return appendFormat.format(part.content); 177 } 178 179 case "escapedValue": 180 { 181 return escapedFormat.format("`" ~ part.content ~ "`"); 182 } 183 184 case "expressionEscaped": 185 { 186 return escapedFormat.format(part.content); 187 } 188 189 default: 190 { 191 return appendFormat.format("`" ~ part.content ~ "`"); 192 } 193 } 194 } 195 196 /** 197 * Parses the meta content of a view. 198 * Params: 199 * part = The part of the meta content. 200 * viewClassMembersGeneration = The resulting string of the view's class members. 201 * viewConstructorGeneration = The resulting string of the view's constructor. 202 * viewModelGenerateGeneration = The resulting string of the view's model-generate function. 203 * viewPlaceHolderGeneration = The resulting string of the view's placeholder generation. 204 * useBaseView = Boolean determining whether the view should use the base view for controllers. 205 * hasController = Boolean determining whether the view has a controller or not. 206 * route = The name of the view's route. (null if no route or if stand-alone.) 207 */ 208 void parseMetaContent(Part part, 209 string viewName, 210 ref string viewClassMembersGeneration, 211 ref string viewConstructorGeneration, 212 ref string viewModelGenerateGeneration, 213 ref string viewPlaceHolderGeneration, 214 ref bool useBaseView, 215 ref bool hasController, 216 ref string route) 217 { 218 string[string] metaData; 219 auto metaContent = part.content.replace("\r", "").split("---"); 220 221 foreach (entry; metaContent) 222 { 223 if (entry && entry.length) 224 { 225 import std..string : indexOf; 226 227 auto keyIndex = entry.indexOf(':'); 228 auto key = entry[0 .. keyIndex].strip().replace("\n", ""); 229 230 metaData[key] = entry[keyIndex + 1 .. $].strip(); 231 } 232 } 233 234 foreach (key, value; metaData) 235 { 236 if (!value || !value.length) 237 { 238 continue; 239 } 240 241 switch (key) 242 { 243 case "placeHolders": 244 { 245 viewPlaceHolderGeneration = placeHolderFormat.format(value); 246 break; 247 } 248 249 case "route": 250 { 251 import std..string : toLower; 252 route = value.replace("\n", "").toLower(); 253 break; 254 } 255 256 case "model": 257 { 258 viewModelGenerateGeneration = modelGenerateFormat.format(value); 259 viewClassMembersGeneration ~= modelMemberFormat.format(value); 260 viewClassMembersGeneration ~= updateModelFromRenderViewFormat.format(viewName); 261 break; 262 } 263 264 static if (isWebServer) 265 { 266 case "controllerUseBaseView": 267 { 268 useBaseView = to!bool(value); 269 break; 270 } 271 272 case "controller": 273 { 274 hasController = true; 275 viewClassMembersGeneration ~= controllerMemberFormat.format(value, useBaseView ? "View" : "view_" ~ viewName); 276 viewConstructorGeneration ~= controllerConstructorFormat.format(value, useBaseView ? "View" : "view_" ~ viewName); 277 break; 278 } 279 } 280 281 case "layout": 282 { 283 viewConstructorGeneration ~= layoutConstructorFormat.format(value.replace("\n", "")); 284 break; 285 } 286 287 case "cache": 288 { 289 if (to!bool(value)) 290 { 291 viewConstructorGeneration ~= "super.cached = true;\r\n"; 292 } 293 294 break; 295 } 296 297 default: break; 298 } 299 } 300 } 301 }