diff --git a/OliverBooth.Blog/Services/TemplateService.cs b/OliverBooth.Blog/Services/TemplateService.cs new file mode 100644 index 0000000..394753e --- /dev/null +++ b/OliverBooth.Blog/Services/TemplateService.cs @@ -0,0 +1,70 @@ +using System.Buffers.Binary; +using Microsoft.EntityFrameworkCore; +using OliverBooth.Blog.Data; +using OliverBooth.Common.Formatting; +using OliverBooth.Common.Markdown; +using OliverBooth.Common.Services; +using SmartFormat; +using SmartFormat.Extensions; + +namespace OliverBooth.Blog.Services; + +/// +/// Represents a service that renders MediaWiki-style templates. +/// +internal sealed class TemplateService : ITemplateService +{ + private static readonly Random Random = new(); + private readonly IDbContextFactory _webContextFactory; + private readonly SmartFormatter _formatter; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The factory. + public TemplateService(IServiceProvider serviceProvider, IDbContextFactory webContextFactory) + { + _formatter = Smart.CreateDefaultSmartFormat(); + _formatter.AddExtensions(new DefaultSource()); + _formatter.AddExtensions(new ReflectionSource()); + _formatter.AddExtensions(new DateFormatter()); + _formatter.AddExtensions(new MarkdownFormatter(serviceProvider)); + + _webContextFactory = webContextFactory; + } + + /// + public string RenderTemplate(TemplateInline templateInline) + { + if (templateInline is null) throw new ArgumentNullException(nameof(templateInline)); + + using BlogContext webContext = _webContextFactory.CreateDbContext(); + Template? template = webContext.Templates.Find(templateInline.Name); + if (template is null) + { + return $"{{{{{templateInline.Name}}}}}"; + } + + Span randomBytes = stackalloc byte[20]; + Random.NextBytes(randomBytes); + + var formatted = new + { + templateInline.ArgumentList, + templateInline.ArgumentString, + templateInline.Params, + RandomInt = BinaryPrimitives.ReadInt32LittleEndian(randomBytes[..4]), + RandomGuid = new Guid(randomBytes[4..]).ToString("N"), + }; + + try + { + return _formatter.Format(template.FormatString, formatted); + } + catch + { + return $"{{{{{templateInline.Name}|{templateInline.ArgumentString}}}}}"; + } + } +}