blob: db07b9d8c1df619ab043210ff562e3e1ae77a087 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
using Morestachio;
using Morestachio.Framework.IO.SingleStream;
using Morestachio.Rendering;
namespace Jellyfin.Server.ServerSetupApp;
/// <summary>
/// Compiles and renders the startup UI Morestachio template.
/// Shared by the live <see cref="SetupServer"/> and the standalone startup UI preview tool so both
/// exercise the exact same template and formatters.
/// </summary>
public sealed class StartupUiRenderer
{
private readonly IRenderer _renderer;
private StartupUiRenderer(IRenderer renderer)
{
_renderer = renderer;
}
/// <summary>
/// Compiles the startup UI template located at <paramref name="templatePath"/>.
/// </summary>
/// <param name="templatePath">The full path to the <c>index.mstemplate.html</c> template.</param>
/// <returns>A ready to use <see cref="StartupUiRenderer"/>.</returns>
public static async Task<StartupUiRenderer> CreateAsync(string templatePath)
{
var fileTemplate = await File.ReadAllTextAsync(templatePath).ConfigureAwait(false);
var renderer = (await ParserOptionsBuilder.New()
.WithTemplate(fileTemplate)
.WithFormatter(
(Version version, int arg) =>
{
// version type does not for some stupid reason implement IFormattable which morestachio relies on for ToString support therefor we need to do it manually.
return version.ToString(arg);
},
"ToString")
.WithFormatter(
(StartupLogTopic logEntry, IEnumerable<StartupLogTopic> children) =>
{
if (children.Any())
{
var maxLevel = logEntry.LogLevel;
var stack = new Stack<StartupLogTopic>(children);
while (maxLevel != LogLevel.Error && stack.Count > 0 && (logEntry = stack.Pop()) is not null) // error is the highest inherted error level.
{
maxLevel = maxLevel < logEntry.LogLevel ? logEntry.LogLevel : maxLevel;
foreach (var child in logEntry.Children)
{
stack.Push(child);
}
}
return maxLevel;
}
return logEntry.LogLevel;
},
"FormatLogLevel")
.WithFormatter(
(LogLevel logLevel) =>
{
switch (logLevel)
{
case LogLevel.Trace:
case LogLevel.Debug:
case LogLevel.None:
return "success";
case LogLevel.Information:
return "info";
case LogLevel.Warning:
return "warn";
case LogLevel.Error:
return "danger";
case LogLevel.Critical:
return "danger-strong";
}
return string.Empty;
},
"ToString")
.BuildAndParseAsync()
.ConfigureAwait(false))
.CreateCompiledRenderer();
return new StartupUiRenderer(renderer);
}
/// <summary>
/// Renders the template with the provided model into the target stream.
/// </summary>
/// <param name="model">The values made available to the template.</param>
/// <param name="output">The stream the rendered HTML is written to.</param>
/// <returns>A Task.</returns>
public Task RenderAsync(IDictionary<string, object> model, Stream output)
{
return _renderer.RenderAsync(
model,
new ByteCounterStream(output, IODefaults.FileStreamBufferSize, true, _renderer.ParserOptions));
}
}
|