diff --git a/API/Protocol/ContentType.cs b/API/Protocol/ContentType.cs
index 65e134fa..c05550bc 100644
--- a/API/Protocol/ContentType.cs
+++ b/API/Protocol/ContentType.cs
@@ -23,6 +23,16 @@ public enum ContentType
///
TextCss,
+ ///
+ /// A markdown file.
+ ///
+ TextMarkdown,
+
+ ///
+ /// A scriban file.
+ ///
+ TextScriban,
+
///
/// A JavaScript source file.
///
@@ -245,6 +255,7 @@ public class FlexibleContentType
{
{ ContentType.TextHtml, "text/html" },
{ ContentType.TextCss, "text/css" },
+ { ContentType.TextMarkdown, "text/markdown" },
{ ContentType.ApplicationJavaScript, "application/javascript" },
{ ContentType.ImageIcon, "image/x-icon" },
{ ContentType.ImageGif, "image/gif" },
diff --git a/Modules/AutoLayout/GenHTTP.Modules.AutoLayout.csproj b/Modules/AutoLayout/GenHTTP.Modules.AutoLayout.csproj
index 2043d2e7..06ddaeff 100644
--- a/Modules/AutoLayout/GenHTTP.Modules.AutoLayout.csproj
+++ b/Modules/AutoLayout/GenHTTP.Modules.AutoLayout.csproj
@@ -45,7 +45,11 @@
+
+
+
+
diff --git a/Modules/AutoLayout/IResourceHandlerProvider.cs b/Modules/AutoLayout/IResourceHandlerProvider.cs
new file mode 100644
index 00000000..76e4abe1
--- /dev/null
+++ b/Modules/AutoLayout/IResourceHandlerProvider.cs
@@ -0,0 +1,17 @@
+using System.Threading.Tasks;
+using GenHTTP.Api.Content;
+using GenHTTP.Api.Content.IO;
+
+namespace GenHTTP.Modules.AutoLayout
+{
+
+ public interface IResourceHandlerProvider
+ {
+
+ public bool Supports(IResource resource);
+
+ ValueTask GetHandlerAsync(IResource resource);
+
+ }
+
+}
diff --git a/Modules/AutoLayout/Provider/DownloadProvider.cs b/Modules/AutoLayout/Provider/DownloadProvider.cs
new file mode 100644
index 00000000..9a206f13
--- /dev/null
+++ b/Modules/AutoLayout/Provider/DownloadProvider.cs
@@ -0,0 +1,18 @@
+using System.Threading.Tasks;
+using GenHTTP.Api.Content;
+using GenHTTP.Api.Content.IO;
+using GenHTTP.Modules.IO;
+
+namespace GenHTTP.Modules.AutoLayout.Provider
+{
+
+ public class DownloadProvider : IResourceHandlerProvider
+ {
+
+ public bool Supports(IResource resource) => true;
+
+ public ValueTask GetHandlerAsync(IResource resource) => new(Content.From(resource));
+
+ }
+
+}
diff --git a/Modules/AutoLayout/Provider/MarkdownProvider.cs b/Modules/AutoLayout/Provider/MarkdownProvider.cs
new file mode 100644
index 00000000..64db7759
--- /dev/null
+++ b/Modules/AutoLayout/Provider/MarkdownProvider.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Threading.Tasks;
+using GenHTTP.Api.Content;
+using GenHTTP.Api.Content.IO;
+using GenHTTP.Api.Protocol;
+using GenHTTP.Modules.Basics;
+using GenHTTP.Modules.Markdown;
+
+namespace GenHTTP.Modules.AutoLayout.Provider
+{
+
+ public class MarkdownProvider : IResourceHandlerProvider
+ {
+
+ public bool Supports(IResource resource) => (resource.ContentType?.KnownType ?? resource.Name?.GuessContentType()) == ContentType.TextMarkdown;
+
+ public ValueTask GetHandlerAsync(IResource resource)
+ {
+ return new(ModMarkdown.Page(resource));
+ }
+
+ }
+
+}
diff --git a/Modules/AutoLayout/Provider/ScribanProvider.cs b/Modules/AutoLayout/Provider/ScribanProvider.cs
new file mode 100644
index 00000000..f468a11e
--- /dev/null
+++ b/Modules/AutoLayout/Provider/ScribanProvider.cs
@@ -0,0 +1,25 @@
+using System.Threading.Tasks;
+
+using GenHTTP.Api.Content;
+using GenHTTP.Api.Content.IO;
+using GenHTTP.Api.Protocol;
+
+using GenHTTP.Modules.Basics;
+using GenHTTP.Modules.Scriban;
+
+namespace GenHTTP.Modules.AutoLayout.Provider
+{
+
+ public class ScribanProvider : IResourceHandlerProvider
+ {
+
+ public bool Supports(IResource resource) => (resource.ContentType?.KnownType ?? resource.Name?.GuessContentType()) == ContentType.TextScriban;
+
+ public ValueTask GetHandlerAsync(IResource resource)
+ {
+ return new(ModScriban.Page(resource));
+ }
+
+ }
+
+}
diff --git a/Modules/AutoLayout/Resolvers.cs b/Modules/AutoLayout/Resolvers.cs
new file mode 100644
index 00000000..e3aceade
--- /dev/null
+++ b/Modules/AutoLayout/Resolvers.cs
@@ -0,0 +1,21 @@
+using GenHTTP.Modules.AutoLayout.Provider;
+using GenHTTP.Modules.AutoLayout.Scanning;
+
+namespace GenHTTP.Modules.AutoLayout
+{
+
+ public static class Resolvers
+ {
+
+ public static HandlerRegistryBuilder Default()
+ {
+ return new HandlerRegistryBuilder().Fallback(new DownloadProvider())
+ .Add(new MarkdownProvider())
+ .Add(new ScribanProvider());
+ }
+
+ public static HandlerRegistryBuilder Empty() => new();
+
+ }
+
+}
diff --git a/Modules/AutoLayout/Scanning/HandlerRegistry.cs b/Modules/AutoLayout/Scanning/HandlerRegistry.cs
new file mode 100644
index 00000000..1becb258
--- /dev/null
+++ b/Modules/AutoLayout/Scanning/HandlerRegistry.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using GenHTTP.Api.Content;
+using GenHTTP.Api.Content.IO;
+
+namespace GenHTTP.Modules.AutoLayout.Scanning
+{
+
+ public class HandlerRegistry
+ {
+
+ #region Get-/Setters
+
+ public List Providers { get; }
+
+ public IResourceHandlerProvider Fallback { get; }
+
+ #endregion
+
+ #region Initialization
+
+ public HandlerRegistry(List providers, IResourceHandlerProvider fallback)
+ {
+ Providers = providers;
+ Fallback = fallback;
+ }
+
+ #endregion
+
+ #region Functionality
+
+ public async ValueTask ResolveAsync(IResource resource)
+ {
+ foreach (var provider in Providers)
+ {
+ if (provider.Supports(resource))
+ {
+ return await provider.GetHandlerAsync(resource);
+ }
+ }
+
+ if (!Fallback.Supports(resource))
+ {
+ throw new InvalidOperationException($"Fallback cannot handle resource '{resource.Name}'");
+ }
+
+ return await Fallback.GetHandlerAsync(resource);
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/Modules/AutoLayout/Scanning/HandlerRegistryBuilder.cs b/Modules/AutoLayout/Scanning/HandlerRegistryBuilder.cs
new file mode 100644
index 00000000..bf4e4d15
--- /dev/null
+++ b/Modules/AutoLayout/Scanning/HandlerRegistryBuilder.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+using GenHTTP.Api.Infrastructure;
+
+namespace GenHTTP.Modules.AutoLayout.Scanning
+{
+
+ public class HandlerRegistryBuilder : IBuilder
+ {
+ private readonly List _Providers = new();
+
+ private IResourceHandlerProvider? _Fallback;
+
+ #region Functionality
+
+ public HandlerRegistryBuilder Add(IResourceHandlerProvider provider)
+ {
+ _Providers.Add(provider);
+ return this;
+ }
+
+ public HandlerRegistryBuilder Fallback(IResourceHandlerProvider provider)
+ {
+ _Fallback = provider;
+ return this;
+ }
+
+ public HandlerRegistry Build()
+ {
+ return new HandlerRegistry(_Providers, _Fallback ?? throw new BuilderMissingPropertyException("fallback"));
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/Modules/AutoLayout/Scanning/HandlerResolver.cs b/Modules/AutoLayout/Scanning/HandlerResolver.cs
deleted file mode 100644
index 3bb8025d..00000000
--- a/Modules/AutoLayout/Scanning/HandlerResolver.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using GenHTTP.Api.Content;
-using GenHTTP.Api.Content.IO;
-using GenHTTP.Api.Protocol;
-
-using GenHTTP.Modules.Basics;
-using GenHTTP.Modules.IO;
-
-namespace GenHTTP.Modules.AutoLayout.Scanning
-{
-
- public static class HandlerResolver
- {
-
- public static IHandlerBuilder Resolve(IResource resource)
- {
- var type = resource.ContentType ?? FlexibleContentType.Get(resource.Name?.GuessContentType() ?? ContentType.ApplicationForceDownload);
-
- switch (type)
- {
- default: return Content.From(resource);
- }
- }
-
- }
-
-}
diff --git a/Modules/AutoLayout/Scanning/TreeScanner.cs b/Modules/AutoLayout/Scanning/TreeScanner.cs
index 0c721815..b5e50200 100644
--- a/Modules/AutoLayout/Scanning/TreeScanner.cs
+++ b/Modules/AutoLayout/Scanning/TreeScanner.cs
@@ -12,22 +12,25 @@ namespace GenHTTP.Modules.AutoLayout.Scanning
public static class TreeScanner
{
- public static ValueTask ScanAsync(IResourceTree tree, params string[] indexNames) => ScanContainerAsync(tree, indexNames);
+ public static ValueTask ScanAsync(IResourceTree tree, HandlerRegistry registry, params string[] indexNames)
+ {
+ return ScanContainerAsync(tree, registry, indexNames);
+ }
- private static async ValueTask ScanContainerAsync(IResourceContainer container, params string[] indexNames)
+ private static async ValueTask ScanContainerAsync(IResourceContainer container, HandlerRegistry registry, params string[] indexNames)
{
var layout = Layout.Create();
await foreach (var node in container.GetNodes())
{
- layout.Add(node.Name, await ScanContainerAsync(node));
+ layout.Add(node.Name, await ScanContainerAsync(node, registry));
}
await foreach (var resource in container.GetResources())
{
if (resource.Name is not null)
{
- var handler = HandlerResolver.Resolve(resource);
+ var handler = await registry.ResolveAsync(resource);
var fileName = Path.GetFileNameWithoutExtension(resource.Name).ToLowerInvariant();
diff --git a/Modules/Basics/CoreExtensions.cs b/Modules/Basics/CoreExtensions.cs
index 493607b0..17ece2e1 100644
--- a/Modules/Basics/CoreExtensions.cs
+++ b/Modules/Basics/CoreExtensions.cs
@@ -81,6 +81,15 @@ public static bool HasType(this IRequest request, params RequestMethod[] methods
{ "cfg", ContentType.TextPlain },
{ "conf", ContentType.TextPlain },
{ "config", ContentType.TextPlain },
+ // Markdown
+ { "md", ContentType.TextMarkdown },
+ // Scriban
+ { "scriban-html", ContentType.TextScriban },
+ { "criban-htm", ContentType.TextScriban },
+ { "sbn-html", ContentType.TextScriban },
+ { "sbn-htm", ContentType.TextScriban },
+ { "sbnhtml", ContentType.TextScriban },
+ { "sbnhtm", ContentType.TextScriban },
// Fonts
{ "eot", ContentType.FontEmbeddedOpenTypeFont },
{ "ttf", ContentType.FontTrueTypeFont },