/// <param name="compiler"></param> /// <inheritdoc /> public virtual CompilationAsync Compile(IDocumentCompiler compiler) { var elseChildren = GetNestedElseConditions() .Select(e => new IfExecutionContainer() { Callback = compiler.Compile(e.Children), Expression = e.MorestachioExpression.Compile() }).ToArray(); var elseDocument = GetNestedElse(); CompilationAsync elseBlock = null; if (elseDocument != null) { elseBlock = compiler.Compile(new[] { elseDocument }); } var children = compiler.Compile(GetIfContents()); var expression = MorestachioExpression.Compile(); return(async(stream, context, scopeData) => { var c = await expression(context, scopeData); context = context.IsNaturalContext || context.Parent == null ? context : context.Parent; if (c.Exists() != Inverted) { await children(stream, context, scopeData); return; } else if (elseChildren.Length > 0) { foreach (var ifExecutionContainer in elseChildren) { if ((await ifExecutionContainer.Expression(context, scopeData)).Exists() == Inverted) { continue; } await ifExecutionContainer.Callback(stream, context, scopeData); return; } } if (elseBlock != null) { await elseBlock(stream, context, scopeData); return; } }); }
/// <summary> /// Helper for managing cancellation, Timeout and result creation. /// </summary> protected virtual async MorestachioDocumentResultPromise Render(object data, CancellationToken token, CompilationAsync executer, IByteCounterStream targetStream) { PreRender(); var timeoutCancellation = new CancellationTokenSource(); if (ParserOptions.Timeout != TimeSpan.Zero) { timeoutCancellation.CancelAfter(ParserOptions.Timeout); var anyCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutCancellation.Token); token = anyCancellationToken.Token; } PerformanceProfiler profiler = null; using (var byteCounterStream = targetStream ?? ParserOptions.StreamFactory.GetByteCounterStream(ParserOptions)) { var context = ParserOptions.CreateContextObject("", data); var scopeData = new ScopeData(ParserOptions, token); try { if (ParserOptions.ProfileExecution) { scopeData.Profiler = profiler = new PerformanceProfiler(true); } await executer(byteCounterStream, context, scopeData); if (timeoutCancellation.IsCancellationRequested) { throw new TimeoutException( $"The requested timeout of '{ParserOptions.Timeout:g}' for template generation was reached."); } PostRender(); } finally { if (!CaptureVariables) { scopeData.Dispose(); scopeData.Variables.Clear(); } } return(new MorestachioDocumentResult(byteCounterStream, profiler, scopeData.Variables.ToDictionary(e => e.Key, e => scopeData.GetFromVariable(null, e.Value)?.Value))); } }
/// <summary> /// Compiles all <see cref="IDocumentItem"/> and their children. If the <see cref="IDocumentItem"/> supports the <see cref="ISupportCustomAsyncCompilation"/> it is used otherwise /// the items <see cref="IDocumentItem.Render"/> method is wrapped /// </summary> /// <param name="documentItems"></param> /// <returns></returns> public CompilationAsync CompileItemsAndChildren(IEnumerable <IDocumentItem> documentItems) { var docs = documentItems.ToArray(); var actions = new Delegate[docs.Length]; for (var index = 0; index < docs.Length; index++) { var documentItem = docs[index]; var document = documentItem; if (document is ISupportCustomAsyncCompilation customAsyncCompilation) { actions[index] = (customAsyncCompilation.Compile(this)); } else if (document is ISupportCustomCompilation customCompilation) { actions[index] = (customCompilation.Compile(this)); } else { actions[index] = new CompilationAsync((async(outputStream, context, scopeData) => { var children = await document.Render(outputStream, context, scopeData); foreach (var documentItemExecution in children) { await MorestachioDocument.ProcessItemsAndChildren(new[] { documentItemExecution.DocumentItem }, outputStream, documentItemExecution.ContextObject, scopeData); } })); } } return(async(stream, context, data) => { if (!data.IsOutputLimited) { for (int i = 0; i < docs.Length; i++) { var action = actions[i]; if (action is CompilationAsync ca) { await ca(stream, context, data); } else if (action is Compilation c) { c(stream, context, data); } } } else { for (int i = 0; i < docs.Length; i++) { if (!DocumentItemBase.ContinueBuilding(stream, data)) { return; } var action = actions[i]; if (action is CompilationAsync ca) { await ca(stream, context, data); } else if (action is Compilation c) { c(stream, context, data); } } } }); }
private async Task CoreAction(IByteCounterStream outputStream, ContextObject context, ScopeData scopeData, CompilationAsync action) { var index = 0; while (ContinueBuilding(outputStream, scopeData)) { var collectionContext = new ContextCollection(index++, false, context.Key, context.Parent, context.Value); //TODO get a way how to execute this on the caller await action(outputStream, collectionContext, scopeData); if (!(await MorestachioExpression.GetValue(collectionContext, scopeData)).Exists()) { break; } } }
/// <summary> /// Compiles the <see cref="Renderer.Document"/> and stores the result /// </summary> public void PreCompile() { CompiledDocument = _compiler.Compile(Document); }