| | 1 | | /* |
| | 2 | | * Copyright (c) 2024 Willy Alberto Kuster |
| | 3 | | * |
| | 4 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| | 5 | | * of this software and associated documentation files (the "Software"), to deal |
| | 6 | | * in the Software without restriction, including without limitation the rights |
| | 7 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| | 8 | | * copies of the Software, and to permit persons to whom the Software is |
| | 9 | | * furnished to do so, subject to the following conditions: |
| | 10 | | * |
| | 11 | | * The above copyright notice and this permission notice shall be included in |
| | 12 | | * all copies or substantial portions of the Software. |
| | 13 | | * |
| | 14 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| | 15 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| | 16 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| | 17 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| | 18 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| | 19 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| | 20 | | * THE SOFTWARE. |
| | 21 | | */ |
| | 22 | | using Scriban; |
| | 23 | | using Scriban.Runtime; |
| | 24 | | using System; |
| | 25 | | using System.Collections.Generic; |
| | 26 | | using System.Linq; |
| | 27 | | using UnityObject = UnityEngine.Object; |
| | 28 | |
|
| | 29 | | namespace Willykc.Templ.Editor.Scaffold |
| | 30 | | { |
| | 31 | | using Abstraction; |
| | 32 | | using static TemplSettings; |
| | 33 | |
|
| | 34 | | internal sealed class TemplScaffoldCore : ITemplScaffoldCore |
| | 35 | | { |
| | 36 | | internal const string ScaffoldGenerationTitle = "Templ Scaffold Generation"; |
| | 37 | |
|
| | 38 | | private const string ProgressBarValidatingInfo = "Validating..."; |
| | 39 | | private const string ProgressBarGeneratingInfo = "Generating..."; |
| | 40 | |
|
| | 41 | | private readonly IFileSystem fileSystem; |
| | 42 | | private readonly ILogger log; |
| | 43 | | private readonly IEditorUtility editorUtility; |
| | 44 | | private readonly List<Type> functions; |
| | 45 | | private readonly string[] functionConflicts; |
| | 46 | | private readonly string[] invalidFunctionNames; |
| | 47 | |
|
| 38 | 48 | | internal TemplScaffoldCore( |
| | 49 | | IFileSystem fileSystem, |
| | 50 | | ILogger log, |
| | 51 | | IEditorUtility editorUtility, |
| | 52 | | ITemplateFunctionProvider templateFunctionProvider) |
| 38 | 53 | | { |
| 38 | 54 | | this.fileSystem = fileSystem ?? |
| | 55 | | throw new ArgumentNullException(nameof(fileSystem)); |
| 38 | 56 | | this.log = log ?? |
| | 57 | | throw new ArgumentNullException(nameof(log)); |
| 38 | 58 | | this.editorUtility = editorUtility ?? |
| | 59 | | throw new ArgumentNullException(nameof(editorUtility)); |
| 38 | 60 | | templateFunctionProvider = templateFunctionProvider ?? |
| | 61 | | throw new ArgumentNullException(nameof(templateFunctionProvider)); |
| | 62 | |
|
| 38 | 63 | | functions = templateFunctionProvider.GetTemplateFunctionTypes().ToList(); |
| 38 | 64 | | functionConflicts = templateFunctionProvider.GetDuplicateTemplateFunctionNames(); |
| 38 | 65 | | invalidFunctionNames = templateFunctionProvider.GetTemplateFunctionNames() |
| 2 | 66 | | .Where(fn => ReservedKeywords.Contains(fn)) |
| | 67 | | .ToArray(); |
| | 68 | |
|
| 38 | 69 | | if (functionConflicts.Length > 0) |
| 6 | 70 | | { |
| 6 | 71 | | log.Error("Function name conflicts detected: " + |
| | 72 | | string.Join(", ", functionConflicts)); |
| 6 | 73 | | } |
| | 74 | |
|
| 38 | 75 | | if(invalidFunctionNames.Length > 0) |
| 2 | 76 | | { |
| 2 | 77 | | log.Error($"Reserved keyword(s) used as function name: " + |
| | 78 | | string.Join(", ", invalidFunctionNames)); |
| 2 | 79 | | } |
| 38 | 80 | | } |
| | 81 | |
|
| | 82 | | string[] ITemplScaffoldCore.GenerateScaffold( |
| | 83 | | TemplScaffold scaffold, |
| | 84 | | string targetPath, |
| | 85 | | object input, |
| | 86 | | UnityObject selection, |
| | 87 | | string[] skipPaths) |
| 15 | 88 | | { |
| 15 | 89 | | skipPaths ??= EmptyStringArray; |
| 15 | 90 | | var errors = ValidateScaffoldGeneration(scaffold, targetPath, input, selection); |
| | 91 | |
|
| 20 | 92 | | if (errors.Count(e => e.Type != TemplScaffoldErrorType.Overwrite) > 0) |
| 5 | 93 | | { |
| 5 | 94 | | log.Error($"Found errors when generating {scaffold.name} scaffold at {targetPath}"); |
| 5 | 95 | | return EmptyStringArray; |
| | 96 | | } |
| | 97 | |
|
| 10 | 98 | | var paths = new List<string>(); |
| | 99 | |
|
| 10 | 100 | | var showIncrement = |
| | 101 | | GetShowProgressIncrementAction(scaffold.Root.NodeCount, ProgressBarGeneratingInfo); |
| | 102 | |
|
| | 103 | | try |
| 10 | 104 | | { |
| 10 | 105 | | GenerateScaffoldTree(scaffold.Root, targetPath, showIncrement, paths, skipPaths); |
| 10 | 106 | | } |
| | 107 | | finally |
| 10 | 108 | | { |
| 10 | 109 | | editorUtility.ClearProgressBar(); |
| 10 | 110 | | } |
| | 111 | |
|
| 10 | 112 | | log.Info($"{scaffold.name} scaffold generated successfully at {targetPath}"); |
| 10 | 113 | | return paths.ToArray(); |
| 15 | 114 | | } |
| | 115 | |
|
| | 116 | | TemplScaffoldError[] ITemplScaffoldCore.ValidateScaffoldGeneration( |
| | 117 | | TemplScaffold scaffold, |
| | 118 | | string targetPath, |
| | 119 | | object input, |
| | 120 | | UnityObject selection) => |
| 19 | 121 | | ValidateScaffoldGeneration(scaffold, targetPath, input, selection); |
| | 122 | |
|
| | 123 | | private TemplScaffoldError[] ValidateScaffoldGeneration( |
| | 124 | | TemplScaffold scaffold, |
| | 125 | | string targetPath, |
| | 126 | | object input = null, |
| | 127 | | UnityObject selection = null) |
| 34 | 128 | | { |
| 34 | 129 | | scaffold = scaffold |
| | 130 | | ? scaffold |
| | 131 | | : throw new ArgumentException($"{nameof(scaffold)} must not be null"); |
| 34 | 132 | | targetPath = !string.IsNullOrWhiteSpace(targetPath) |
| | 133 | | ? targetPath |
| | 134 | | : throw new ArgumentException($"{nameof(targetPath)} must not be null or empty"); |
| | 135 | |
|
| 34 | 136 | | var validationContext = new ValidationContext() |
| | 137 | | { |
| | 138 | | rootPath = targetPath, |
| | 139 | | seed = Guid.NewGuid().ToString(), |
| | 140 | | input = input, |
| | 141 | | path = targetPath, |
| | 142 | | selection = selection, |
| | 143 | | errors = new List<TemplScaffoldError>() |
| | 144 | | }; |
| | 145 | |
|
| 34 | 146 | | CollectTemplateFunctionNameErrors(validationContext); |
| 34 | 147 | | ProcessDynamicScaffold(scaffold, validationContext); |
| 34 | 148 | | CheckForEmptyScaffoldTree(scaffold, validationContext); |
| | 149 | |
|
| 34 | 150 | | if (validationContext.errors.Count > 0) |
| 9 | 151 | | { |
| 9 | 152 | | return validationContext.errors.ToArray(); |
| | 153 | | } |
| | 154 | |
|
| 25 | 155 | | var showProgressIncrement = |
| | 156 | | GetShowProgressIncrementAction(scaffold.Root.NodeCount, ProgressBarValidatingInfo); |
| | 157 | |
|
| | 158 | | try |
| 25 | 159 | | { |
| 25 | 160 | | CollectScaffoldErrors(scaffold.Root, validationContext, showProgressIncrement); |
| 25 | 161 | | } |
| | 162 | | finally |
| 25 | 163 | | { |
| 25 | 164 | | editorUtility.ClearProgressBar(); |
| 25 | 165 | | } |
| | 166 | |
|
| 25 | 167 | | return validationContext.errors.ToArray(); |
| 34 | 168 | | } |
| | 169 | |
|
| | 170 | | private void CheckForEmptyScaffoldTree( |
| | 171 | | TemplScaffold scaffold, |
| | 172 | | ValidationContext validationContext) |
| 34 | 173 | | { |
| 34 | 174 | | if (scaffold.Root?.Children.Count == 0) |
| 1 | 175 | | { |
| 1 | 176 | | AddError(validationContext.errors, $"Found empty tree for scaffold {scaffold.name}", |
| | 177 | | TemplScaffoldErrorType.Undefined); |
| 1 | 178 | | } |
| 34 | 179 | | } |
| | 180 | |
|
| | 181 | | private void CollectTemplateFunctionNameErrors(ValidationContext validationContext) |
| 34 | 182 | | { |
| 34 | 183 | | var functionConflictErrors = functionConflicts |
| 3 | 184 | | .Select(c => new TemplScaffoldError(TemplScaffoldErrorType.Undefined, |
| | 185 | | $"Found duplicate template function name: {c}")); |
| 34 | 186 | | var reservedKeywordErrors = invalidFunctionNames |
| 1 | 187 | | .Select(rk => new TemplScaffoldError(TemplScaffoldErrorType.Undefined, |
| | 188 | | $"Found reserved keyword used as function name: {rk}")); |
| 34 | 189 | | validationContext.errors.AddRange(functionConflictErrors); |
| 34 | 190 | | validationContext.errors.AddRange(reservedKeywordErrors); |
| 34 | 191 | | } |
| | 192 | |
|
| | 193 | | private void ProcessDynamicScaffold( |
| | 194 | | TemplScaffold scaffold, |
| | 195 | | ValidationContext validationContext) |
| 34 | 196 | | { |
| 34 | 197 | | if (!(scaffold is TemplDynamicScaffold dynamicScaffold)) |
| 23 | 198 | | { |
| 23 | 199 | | return; |
| | 200 | | } |
| | 201 | |
|
| 11 | 202 | | if (!dynamicScaffold.TreeTemplate || dynamicScaffold.TreeTemplate.HasErrors) |
| 1 | 203 | | { |
| 1 | 204 | | AddError(validationContext.errors, |
| | 205 | | $"Null or invalid tree template for dynamic scaffold {scaffold.name}", |
| | 206 | | TemplScaffoldErrorType.Template); |
| 1 | 207 | | return; |
| | 208 | | } |
| | 209 | |
|
| 10 | 210 | | var templateContext = GetTemplateContext(validationContext); |
| 10 | 211 | | var templateText = dynamicScaffold.TreeTemplate.Text; |
| 10 | 212 | | var renderedText = string.Empty; |
| | 213 | |
|
| 10 | 214 | | if (string.IsNullOrWhiteSpace(templateText)) |
| 1 | 215 | | { |
| 1 | 216 | | AddError(validationContext.errors, |
| | 217 | | $"Empty tree template for dynamic scaffold {scaffold.name}", |
| | 218 | | TemplScaffoldErrorType.Template); |
| 1 | 219 | | return; |
| | 220 | | } |
| | 221 | |
|
| | 222 | | try |
| 9 | 223 | | { |
| 9 | 224 | | var template = Template.Parse(templateText); |
| 9 | 225 | | renderedText = template.Render(templateContext); |
| 8 | 226 | | dynamicScaffold.Deserialize(renderedText); |
| 7 | 227 | | } |
| 2 | 228 | | catch (Exception e) |
| 2 | 229 | | { |
| 2 | 230 | | AddError(validationContext.errors, |
| | 231 | | "Error parsing tree for dynamic scaffold " + |
| | 232 | | $"{scaffold.name}:\n{renderedText}", |
| | 233 | | TemplScaffoldErrorType.Template, e); |
| 2 | 234 | | } |
| 34 | 235 | | } |
| | 236 | |
|
| | 237 | | private Action GetShowProgressIncrementAction(float total, string info) |
| 35 | 238 | | { |
| 35 | 239 | | float progress = 0; |
| | 240 | |
|
| | 241 | | void ShowProgressIncrement() |
| 128 | 242 | | { |
| 128 | 243 | | progress++; |
| 128 | 244 | | editorUtility.DisplayProgressBar(ScaffoldGenerationTitle, |
| | 245 | | info, progress / total); |
| 128 | 246 | | } |
| | 247 | |
|
| 35 | 248 | | return ShowProgressIncrement; |
| 35 | 249 | | } |
| | 250 | |
|
| | 251 | | private void GenerateScaffoldTree( |
| | 252 | | TemplScaffoldNode node, |
| | 253 | | string targetNodePath, |
| | 254 | | Action showProgressIncrement, |
| | 255 | | List<string> paths, |
| | 256 | | string[] skipPaths) |
| 30 | 257 | | { |
| 30 | 258 | | var renderedPath = node is TemplScaffoldRoot |
| | 259 | | ? targetNodePath |
| | 260 | | : $"{targetNodePath}/{node.RenderedName}"; |
| | 261 | |
|
| | 262 | | try |
| 30 | 263 | | { |
| 30 | 264 | | if (node is TemplScaffoldDirectory && !fileSystem.DirectoryExists(renderedPath)) |
| 8 | 265 | | { |
| 8 | 266 | | fileSystem.CreateDirectory(renderedPath); |
| 8 | 267 | | paths.Add(renderedPath); |
| 8 | 268 | | } |
| 22 | 269 | | else if (node is TemplScaffoldFile fileNode && !skipPaths.Contains(renderedPath)) |
| 10 | 270 | | { |
| 10 | 271 | | fileSystem.WriteAllText(renderedPath, fileNode.RenderedTemplate); |
| 9 | 272 | | paths.Add(renderedPath); |
| 9 | 273 | | } |
| 29 | 274 | | } |
| 1 | 275 | | catch (Exception e) |
| 1 | 276 | | { |
| 1 | 277 | | log.Error($"Error creating node {node.NodePath} at {renderedPath}", e); |
| 1 | 278 | | } |
| | 279 | |
|
| 30 | 280 | | showProgressIncrement(); |
| | 281 | |
|
| 130 | 282 | | foreach (var child in node.Children) |
| 20 | 283 | | { |
| 20 | 284 | | GenerateScaffoldTree(child, renderedPath, showProgressIncrement, paths, skipPaths); |
| 20 | 285 | | } |
| 30 | 286 | | } |
| | 287 | |
|
| | 288 | | private void CollectScaffoldErrors( |
| | 289 | | TemplScaffoldNode node, |
| | 290 | | ValidationContext validationContext, |
| | 291 | | Action showProgressIncrement) |
| 98 | 292 | | { |
| 98 | 293 | | var templateContext = GetTemplateContext(validationContext); |
| 98 | 294 | | CollectRenderNameErrors(node, validationContext, templateContext); |
| | 295 | |
|
| 98 | 296 | | validationContext.path = node is TemplScaffoldRoot |
| | 297 | | ? validationContext.path |
| | 298 | | : $"{validationContext.path}/{node.RenderedName}"; |
| | 299 | |
|
| 98 | 300 | | CheckForFileOverwrite(node, validationContext); |
| | 301 | |
|
| 98 | 302 | | if (node is TemplScaffoldFile fileNode && |
| | 303 | | TryGetContext(fileNode, validationContext, out templateContext)) |
| 33 | 304 | | { |
| 33 | 305 | | CollectFileNodeErrors(fileNode, templateContext, validationContext); |
| 33 | 306 | | } |
| 65 | 307 | | else if (node is TemplScaffoldDirectory && node.Children.Count == 0) |
| 3 | 308 | | { |
| 3 | 309 | | AddError(validationContext.errors, $"Empty directory node {node.NodePath}", |
| | 310 | | TemplScaffoldErrorType.Undefined); |
| 3 | 311 | | } |
| | 312 | |
|
| 98 | 313 | | showProgressIncrement(); |
| 98 | 314 | | CheckForDuplicateNodeNames(node, validationContext); |
| | 315 | |
|
| 440 | 316 | | foreach (var child in node.Children) |
| 73 | 317 | | { |
| 73 | 318 | | CollectScaffoldErrors(child, validationContext, showProgressIncrement); |
| 73 | 319 | | } |
| 98 | 320 | | } |
| | 321 | |
|
| | 322 | | private void CheckForDuplicateNodeNames( |
| | 323 | | TemplScaffoldNode node, |
| | 324 | | ValidationContext validationContext) |
| 98 | 325 | | { |
| 98 | 326 | | if (IsNameDuplicated(node)) |
| 4 | 327 | | { |
| 4 | 328 | | AddError(validationContext.errors, |
| | 329 | | "Different sister node with the same name found for node " + |
| | 330 | | node.NodePath, TemplScaffoldErrorType.Filename); |
| 4 | 331 | | } |
| 98 | 332 | | } |
| | 333 | |
|
| | 334 | | private void CollectRenderNameErrors( |
| | 335 | | TemplScaffoldNode node, |
| | 336 | | ValidationContext validationContext, |
| | 337 | | TemplateContext context) |
| 98 | 338 | | { |
| 98 | 339 | | if (!(node is TemplScaffoldRoot)) |
| 73 | 340 | | { |
| 73 | 341 | | node.RenderedName = RenderTemplate(node, node.name, context, |
| | 342 | | TemplScaffoldErrorType.Filename, validationContext.errors); |
| 73 | 343 | | ValidateRenderedName(node, validationContext.errors); |
| 73 | 344 | | } |
| 98 | 345 | | } |
| | 346 | |
|
| | 347 | | private void CheckForFileOverwrite( |
| | 348 | | TemplScaffoldNode node, |
| | 349 | | ValidationContext validationContext) |
| 98 | 350 | | { |
| 98 | 351 | | if (!(node is TemplScaffoldRoot) && fileSystem.FileExists(validationContext.path)) |
| 1 | 352 | | { |
| 1 | 353 | | var error = new TemplScaffoldError(TemplScaffoldErrorType.Overwrite, |
| | 354 | | validationContext.path); |
| 1 | 355 | | validationContext.errors.Add(error); |
| 1 | 356 | | } |
| 98 | 357 | | } |
| | 358 | |
|
| | 359 | | private bool TryGetContext( |
| | 360 | | TemplScaffoldFile fileNode, |
| | 361 | | ValidationContext validationContext, |
| | 362 | | out TemplateContext templateContext) |
| 35 | 363 | | { |
| 35 | 364 | | templateContext = null; |
| | 365 | |
|
| | 366 | | try |
| 35 | 367 | | { |
| 35 | 368 | | templateContext = GetTemplateContext(validationContext, fileNode.NodeInputs); |
| 33 | 369 | | return true; |
| | 370 | | } |
| 2 | 371 | | catch (Exception e) |
| 2 | 372 | | { |
| 2 | 373 | | AddError(validationContext.errors, |
| | 374 | | $"Error preparing context for node {fileNode.NodePath}", |
| | 375 | | TemplScaffoldErrorType.Undefined, e); |
| 2 | 376 | | return false; |
| | 377 | | } |
| 35 | 378 | | } |
| | 379 | |
|
| | 380 | | private void CollectFileNodeErrors( |
| | 381 | | TemplScaffoldFile fileNode, |
| | 382 | | TemplateContext context, |
| | 383 | | ValidationContext validationContext) |
| 33 | 384 | | { |
| 33 | 385 | | if (fileNode.Template && !fileNode.Template.HasErrors) |
| 32 | 386 | | { |
| 32 | 387 | | fileNode.RenderedTemplate = RenderTemplate(fileNode, fileNode.Template.Text, |
| | 388 | | context, TemplScaffoldErrorType.Template, validationContext.errors); |
| 32 | 389 | | } |
| | 390 | | else |
| 1 | 391 | | { |
| 1 | 392 | | AddError(validationContext.errors, |
| | 393 | | $"Null or invalid template found for node {fileNode.NodePath}", |
| | 394 | | TemplScaffoldErrorType.Template); |
| 1 | 395 | | } |
| 33 | 396 | | } |
| | 397 | |
|
| | 398 | | private static bool IsNameDuplicated(TemplScaffoldNode node) => |
| 195 | 399 | | node.Parent?.Children.Any(c => c != node && c.name == node.name) ?? false; |
| | 400 | |
|
| | 401 | | private void ValidateRenderedName( |
| | 402 | | TemplScaffoldNode node, |
| | 403 | | List<TemplScaffoldError> errors) |
| 73 | 404 | | { |
| 73 | 405 | | if (string.IsNullOrWhiteSpace(node.RenderedName)) |
| 3 | 406 | | { |
| 3 | 407 | | AddError(errors, $"Empty {nameof(TemplScaffoldErrorType.Filename)} found for " + |
| | 408 | | $"node {node.NodePath}", TemplScaffoldErrorType.Filename); |
| 3 | 409 | | } |
| | 410 | |
|
| 73 | 411 | | if (!node.RenderedName.IsValidFileName()) |
| 4 | 412 | | { |
| 4 | 413 | | AddError(errors, "Invalid characters found in " + |
| | 414 | | $"{nameof(TemplScaffoldErrorType.Filename)}: {node.RenderedName} for " + |
| | 415 | | $"node {node.NodePath}", TemplScaffoldErrorType.Filename); |
| 4 | 416 | | } |
| 73 | 417 | | } |
| | 418 | |
|
| | 419 | | private TemplateContext GetTemplateContext( |
| | 420 | | ValidationContext validationContext, |
| | 421 | | IDictionary<string, object> nodeInputs = null) |
| 143 | 422 | | { |
| 143 | 423 | | nodeInputs ??= new Dictionary<string, object>(); |
| 143 | 424 | | var scriptObject = new ScriptObject(); |
| 4147 | 425 | | scriptObject.Import(typeof(TemplFunctions), renamer: member => member.Name); |
| 143 | 426 | | functions.ForEach(t => scriptObject.Import(t, renamer: member => member.Name)); |
| 143 | 427 | | scriptObject.Add(InputName, validationContext.input); |
| 143 | 428 | | scriptObject.Add(SelectionName, validationContext.selection); |
| 143 | 429 | | scriptObject.Add(NameOfOutputAssetPath, validationContext.path); |
| 143 | 430 | | scriptObject.Add(SeedName, validationContext.seed); |
| 143 | 431 | | scriptObject.Add(RootPathName, validationContext.rootPath); |
| | 432 | |
|
| 143 | 433 | | var inputCollisions = nodeInputs.Keys.Where(scriptObject.ContainsKey).ToArray(); |
| 143 | 434 | | if (inputCollisions.Length > 0) |
| 2 | 435 | | { |
| 2 | 436 | | throw new InvalidOperationException("Found node input(s) named as reserved " + |
| | 437 | | "keywords: " + string.Join(", ", inputCollisions)); |
| | 438 | | } |
| | 439 | |
|
| 447 | 440 | | foreach (var nodeInput in nodeInputs) |
| 12 | 441 | | { |
| 12 | 442 | | scriptObject.Add(nodeInput.Key, nodeInput.Value); |
| 12 | 443 | | } |
| | 444 | |
|
| 141 | 445 | | var templateContext = new TemplateContext() |
| | 446 | | { |
| | 447 | | TemplateLoader = AssetTemplateLoader.Instance |
| | 448 | | }; |
| | 449 | |
|
| 141 | 450 | | templateContext.PushGlobal(scriptObject); |
| 141 | 451 | | return templateContext; |
| 141 | 452 | | } |
| | 453 | |
|
| | 454 | | private string RenderTemplate( |
| | 455 | | TemplScaffoldNode node, |
| | 456 | | string text, |
| | 457 | | TemplateContext context, |
| | 458 | | TemplScaffoldErrorType errorType, |
| | 459 | | List<TemplScaffoldError> errors) |
| 105 | 460 | | { |
| | 461 | | try |
| 105 | 462 | | { |
| 105 | 463 | | if (!string.IsNullOrWhiteSpace(text)) |
| 104 | 464 | | { |
| 104 | 465 | | var template = Template.Parse(text); |
| 104 | 466 | | return template.Render(context); |
| | 467 | | } |
| | 468 | | else |
| 1 | 469 | | { |
| 1 | 470 | | AddError(errors, $"Empty {errorType} found in node {node.NodePath}", errorType); |
| 1 | 471 | | } |
| 1 | 472 | | } |
| 2 | 473 | | catch (Exception e) |
| 2 | 474 | | { |
| 2 | 475 | | AddError(errors, $"Error rendering {errorType} of node {node.NodePath}", |
| | 476 | | errorType, e); |
| 2 | 477 | | } |
| | 478 | |
|
| 3 | 479 | | return string.Empty; |
| 105 | 480 | | } |
| | 481 | |
|
| | 482 | | private void AddError( |
| | 483 | | List<TemplScaffoldError> errors, |
| | 484 | | string message, |
| | 485 | | TemplScaffoldErrorType type, |
| | 486 | | Exception exception = null) |
| 25 | 487 | | { |
| 25 | 488 | | if (exception != null) |
| 6 | 489 | | { |
| 6 | 490 | | log.Error(message, exception); |
| 6 | 491 | | message = $"{message}: {exception.Message}"; |
| 6 | 492 | | } |
| | 493 | | else |
| 19 | 494 | | { |
| 19 | 495 | | log.Error(message); |
| 19 | 496 | | } |
| | 497 | |
|
| 25 | 498 | | var error = new TemplScaffoldError(type, message); |
| 25 | 499 | | errors.Add(error); |
| 25 | 500 | | } |
| | 501 | |
|
| | 502 | | private struct ValidationContext |
| | 503 | | { |
| | 504 | | internal object input; |
| | 505 | | internal UnityObject selection; |
| | 506 | | internal string path; |
| | 507 | | internal List<TemplScaffoldError> errors; |
| | 508 | | internal string seed; |
| | 509 | | internal string rootPath; |
| | 510 | | } |
| | 511 | | } |
| | 512 | | } |