diff --git a/docs/_data/navigation_docs.yml b/docs/_data/navigation_docs.yml index 25b0abd..d338dd7 100644 --- a/docs/_data/navigation_docs.yml +++ b/docs/_data/navigation_docs.yml @@ -15,5 +15,7 @@ - title: Usage docs: - supported-format-engine + - cli-options - basic-usage - - other-options \ No newline at end of file + - use-stdin-stdout + - specify-engine-parser \ No newline at end of file diff --git a/docs/_docs/cli-options.md b/docs/_docs/cli-options.md new file mode 100644 index 0000000..3fe4ab7 --- /dev/null +++ b/docs/_docs/cli-options.md @@ -0,0 +1,104 @@ +--- +title: CLI options +tags: [quick-start, cli-usage] +--- +## General format for options + +### Shortcut Option (Single Character) + +Shortcut options are prefixed with a single hyphen (-) and can be followed directly by the value without a space, though a space can be used if preferred. +**Example:** + +- Without space: `-tpath/to/template` +- With space: `-t path/to/template` + +### Long Version Option (Double Dash) + +Long options are prefixed with double hyphens (--), the first letter of the option is uppercase, and the option name is followed by an equal sign (=) to assign its value. +**Example:** `--Template=path/to/template` + +## Option Values + +### Single Value + +An option can take a single value, which is provided right after the shortcut or the `=` sign for long options. +**Example**: `-s path/to/source` or `--Source=path/to/source` + +### Switch Values + +If an option accepts a boolean, this one can be omitted. +**Example**: `-i` or `--StdIn` or `--StdIn=true` + +### Multiple Values + +If an option accepts multiple values, the values should be separated by a semicolon (;). +**Example**: `--Sources=file1.yaml;file2.json;file3.xml` + +### Key-Value Pairs + +For key-value pairs, the key is followed by a colon (:), and then the value. Multiple key-value pairs, if allowed, should be separated by a semicolon (;). +**Example**: `--Extensions=txt:handlebars;liquid:fluid` + +## Didot Options Explained + +### Template Option + +Shortcut: `-t` +Long: `--Template` +Description: Specifies the path to the template file. +Accept: single value. +Mandatory: yes. +Example: `-tpath/to/template` or `--Template=path/to/template` + +### Engine option + +Shortcut: -e +Long: --Engine +Description: Specifies the template engine to use (scriban, fluid, dotliquid, handlebars, smartformat, stringtemplate). +Accept: single value. When omitted Didot will select the engine based on the extension of the template file. +Example: `-efluid` or `--Engine=fluid` + +### Source Option + +Shortcut: `-s` +Long: `--Source` +Description: Specifies the path to the source file. If omitted, input can be taken from StdIn. +Accept: single value. +Exclusive: can't be set with the parameter `--StdIn` +Example: `-spath/to/source` or `--Source=path/to/source` + +### Parser Option + +Shortcut: -p +Long: --Parser +Description: Specifies the parser to use (YAML, JSON, XML). +Accept: single value. +Mandatory: no expect if `--StdIn` is specified. When omitted Didot will select the parser based on the extension of the source file +Example: `-pYAML` or `--Parser=YAML` + +### StdIn Option + +Shortcut: `-i` +Long: --StdIn +Description: Specifies the input to the source data as coming from the StdIn. +Accept: switch value. +Exclusive: can't be set with the parameter `--Source` +Example: `-i` or `--StdIn` + +### Output Option + +Shortcut: `-o` +Long: `--Output` +Description: Specifies the path to the generated output file. If omitted, output is rendered to StdOut. +Accept: single value. +Mandatory: no. +Example: `-opath/to/output` or `--Output=path/to/output` + +### Extension option + +Shortcut: -x +Long: --Extension +Description: Specifies additional or replacing link between extensions and engine for automatic detection +Accept: multiple key-value pairs. +Mandatory: no. +Example: `-xtxt:handlebars;liquid:fluid` or `--Extension=txt:handlebars;liquid:fluid` diff --git a/docs/_docs/installation-windows.md b/docs/_docs/installation-windows.md index 8d9ea26..837c967 100644 --- a/docs/_docs/installation-windows.md +++ b/docs/_docs/installation-windows.md @@ -10,7 +10,7 @@ tags: [quick-start, installation] Example: - ```pwsh + ```powershell https://github.com/Seddryck/Didot/releases/download/v0.13.0/Didot-0.13.0-net7.0-win-x64.zip ``` diff --git a/docs/_docs/specify-engine-parser.md b/docs/_docs/specify-engine-parser.md new file mode 100644 index 0000000..aee43a1 --- /dev/null +++ b/docs/_docs/specify-engine-parser.md @@ -0,0 +1,58 @@ +--- +title: Specify template engine or parser +tags: [cli-usage] +--- +## Specify the data parser + +- `-p, --Parser`: Defines the parser to use when the source data is provided through the console. Accepted values are `yaml`, `json` or `xml`. This option is required only when the `--Source` argument is omitted or if the extension of the source file is not recognized to determine the parser. + +```powershell +didot -t template.hbs -s data.txt -p json -o output.txt +``` + +In this example: + +- `template.hbs` is the Handlebars template file. +- `data.txt` is the source file. +- `json` is the parser of input data from the source file. +- `output.txt` is the file where the output will be rendered. + +## Specify the template engine + +### Direct specification + +- `-e, --Engine`: Defines the template engine to use independantly of the template file extension. Accepted values are `scriban`, `dotliquid`, `fluid`, `handlebars`, `stringtemplate`, `smartformat`. + +```powershell +didot -t template.txt -s data.json -e handlebars -o output.txt +``` + +In this example: + +- `template.txt` is the template file. +- `data.json` is the data JSON file. +- `handlebars` is the template engine to use. +- `output.txt` is the file where the output will be rendered. + +### Add or replace extension associations + +- `-x, --Extensions`: Defines the association of a file's extension with a template engine. More than one can be specified. + +```powershell +didot -t template.txt -s data.json -x txt:handlebars;liquid:fluid -o output.txt +``` + +In this example: + +- `template.txt` is the template file. +- `data.json` is the data JSON file. +- `txt:handlebars;liquid:fluid` is associating Handlbars to the extension `.txt` and Fluid to the extension `.liquid`. +- `output.txt` is the file where the output will be rendered. + +By default following file's extension association are registered: + +- `.scriban` to Scriban +- `.liquid` to DotLiquid +- `.hbs` to Handlebars +- `.smart` to SmartFormat +- `.st` and `.stg` to StringTemplate diff --git a/docs/_docs/supported-format-engine.md b/docs/_docs/supported-format-engine.md index 97d39b3..1c7252c 100644 --- a/docs/_docs/supported-format-engine.md +++ b/docs/_docs/supported-format-engine.md @@ -19,11 +19,17 @@ Didot utilizes some templating engines, which allow for powerful and flexible te - Supports customizable scripting with rich expressions and filters. - Can work with JSON and YAML data sources. - Typical Use Case: Config file generation, reports, email templates, or any templating scenario not tied to a specific web framework. -- **Liquid**: Templates with the `.liquid` extension are parsed using a dotLiquid template engine. DotLiquid is a .NET port of the Liquid templating engine used by platforms like Shopify. +- **DotLiquid**: Templates with the `.liquid` extension are parsed using a dotLiquid template engine. DotLiquid is a .NET port of the Liquid templating engine used by platforms like Shopify. - Secure (no access to system objects), making it ideal for user-generated templates. - Allows both dynamic and static templating. - Supports filters, tags, and various control flow structures. - Typical Use Case: SaaS applications, dynamic content rendering, email templates. +- **Fluid**: Fully compatible with `.liquid` templates. The Fluid template engine is a fast and secure .NET-based port of the Liquid templating language. + - Optimized for performance, with careful memory management and faster parsing. + - Highly secure (does not expose system objects) and well-suited for user-generated content and environments requiring strict control over input and output. + - Rich templating features, supporting filters, tags, loops, and conditionals. + - Flexible and customizable, making it easy to extend the engine with custom filters or tags. + - Typical Use Case: Applications with complex data bindings, dynamic content generation in websites, CMS platforms, and document templating systems. - **Handlebars**: Templates with the `.hbs` extension are parsed using a Handlebars template engine. Handlebars C# port of the popular JavaScript Handlebars templating engine. - Simple syntax for generating HTML or text files from templates. - Support for helpers, partial templates, and block helpers. diff --git a/docs/_docs/other-options.md b/docs/_docs/use-stdin-stdout.md similarity index 96% rename from docs/_docs/other-options.md rename to docs/_docs/use-stdin-stdout.md index dfc20af..9b91b6d 100644 --- a/docs/_docs/other-options.md +++ b/docs/_docs/use-stdin-stdout.md @@ -1,5 +1,5 @@ --- -title: Other options +title: Usage of StdIn and StdOut tags: [cli-usage] --- ## With data from the console @@ -8,7 +8,7 @@ tags: [cli-usage] - `-p, --Parser`: Defines the parser to use when the source data is provided through the console. Accepted values are `yaml`, `json` or `xml`. This option is required only when the `--Source` argument is omitted or if the extension of the source file is not recognized to determine the parser. CMD: -```cmd +```bash type data.json | didot --StdIn -t template.hbs -p json -o output.txt ``` diff --git a/src/Didot.Cli/Options.cs b/src/Didot.Cli/Options.cs index bbae77d..a4d81e8 100644 --- a/src/Didot.Cli/Options.cs +++ b/src/Didot.Cli/Options.cs @@ -20,6 +20,12 @@ public class Options [Option('p', "Parser", Required = false, HelpText = "The parser to use when reading from StdIn.")] public string? Parser { get; set; } + [Option('e', "Engine", Required = false, HelpText = "Force a specific engine.")] + public string? Engine { get; set; } + + [Option('x', "Extension", Required = false, Separator = ';', HelpText = "Associate an extension to a specific engine.")] + public required IEnumerable Extensions { get; set; } + [Option('o', "Output", Required = false, HelpText = "Path to the generated file.")] public string? Output { get; set; } } diff --git a/src/Didot.Cli/Program.cs b/src/Didot.Cli/Program.cs index 3fc0303..13b55d8 100644 --- a/src/Didot.Cli/Program.cs +++ b/src/Didot.Cli/Program.cs @@ -49,9 +49,7 @@ static Stream copyInStream() var parserFactory = new FileBasedSourceParserFactory(); var parser = parserFactory.GetSourceParser(sourceExtension); - var templateExtension = new FileInfo(opts.Template).Extension; - var engineFactory = new FileBasedTemplateEngineFactory(); - var engine = engineFactory.GetTemplateEngine(templateExtension); + var engine = GetTemplateEngine(opts); var printer = new Printer(engine, parser); using var template = File.OpenRead(opts.Template); @@ -63,9 +61,42 @@ static Stream copyInStream() File.WriteAllText(opts.Output, output); } + private static ITemplateEngine GetTemplateEngine(Options opts) + { + var engineFactory = new FileBasedTemplateEngineFactory(); + if (opts.Engine is not null) + return engineFactory.GetTemplateEngineByTag(opts.Engine); + else + { + if (opts.Extensions is not null && opts.Extensions.Any()) + { + foreach (var extensionAssociation in opts.Extensions) + { + if (!string.IsNullOrWhiteSpace(extensionAssociation)) + { + var split = extensionAssociation.Split(':'); + if (split.Length != 2) + throw new Exception(extensionAssociation); + (string extension, var engineTag) = (split[0], split[1]); + var engineInstance = engineFactory.GetTemplateEngineByTag(engineTag); + engineFactory.AddOrReplaceEngine(extension, engineInstance); + } + } + } + var templateExtension = new FileInfo(opts.Template).Extension; + return engineFactory.GetTemplateEngineByExtension(templateExtension); + } + } + static void HandleParseError(IEnumerable errs) { - // Handle errors here (e.g., show help message) - System.Console.WriteLine("Error parsing arguments."); + Console.WriteLine("Error parsing arguments."); + foreach (var error in errs) + { + if (error is UnknownOptionError unknown) + Console.WriteLine($"{unknown.Tag}: {unknown.Token}"); + else + Console.WriteLine(error); + } } } diff --git a/src/Didot.Core/Didot.Core.csproj b/src/Didot.Core/Didot.Core.csproj index 4464765..ce20192 100644 --- a/src/Didot.Core/Didot.Core.csproj +++ b/src/Didot.Core/Didot.Core.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs b/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs index 796c0e6..21cad84 100644 --- a/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs +++ b/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs @@ -8,14 +8,73 @@ namespace Didot.Core.TemplateEngines; public class FileBasedTemplateEngineFactory { - public ITemplateEngine GetTemplateEngine(string extension) - => extension.ToLowerInvariant() switch + protected Dictionary engines = new(); + + public FileBasedTemplateEngineFactory() + { + Initialize(); + } + + protected virtual void Initialize() + { + engines.Clear(); + engines.Add(".scriban", new ScribanWrapper()); + engines.Add(".liquid", new DotLiquidWrapper()); + engines.Add(".hbs", new HandlebarsWrapper()); + engines.Add(".smart", new SmartFormatWrapper()); + engines.Add(".st", new StringTemplateWrapper()); + engines.Add(".stg", new StringTemplateWrapper()); + } + + public virtual void AddOrReplaceEngine(string extension, ITemplateEngine engine) + { + extension = NormalizeExtension(extension); + + if (engines.ContainsKey(extension)) + engines[extension] = engine; + else + engines.Add(extension, engine); + } + + protected virtual string NormalizeExtension(string extension) + { + extension = extension.Trim().ToLowerInvariant(); + if (!extension.StartsWith('.')) + extension = $".{extension}"; + return extension; + } + + public ITemplateEngine GetTemplateEngineByExtension(string extension) + { + extension = NormalizeExtension(extension); + if (engines.TryGetValue(extension, out var engine)) + return engine; + throw new NotSupportedException(nameof(extension)); + } + + public Dictionary ListEngineByTags() + { + var asm = typeof(ITemplateEngine).Assembly; + var types = asm.GetTypes() + .Where(t => t.Namespace == GetType().Namespace + && typeof(ITemplateEngine).IsAssignableFrom(t) + && !t.IsInterface + && !t.IsAbstract); + + var dict = new Dictionary(); + foreach (var type in types) { - ".scriban" => new ScribanWrapper(), - ".liquid" => new DotLiquidWrapper(), - ".hbs" => new HandlebarsWrapper(), - ".smart" => new SmartFormatWrapper(), - ".st" or ".stg" => new StringTemplateWrapper(), - _ => throw new NotSupportedException() - }; + var tag = type.Name.Replace("Wrapper", string.Empty).Trim().ToLowerInvariant(); + dict.Add(tag, type); + } + return dict; + } + + public ITemplateEngine GetTemplateEngineByTag(string tag) + { + tag = tag.Trim().ToLowerInvariant(); + if (!ListEngineByTags().TryGetValue(tag, out var engineType)) + throw new Exception(tag); + return (ITemplateEngine)Activator.CreateInstance(engineType)!; + } } diff --git a/src/Didot.Core/TemplateEngines/FluidWrapper.cs b/src/Didot.Core/TemplateEngines/FluidWrapper.cs new file mode 100644 index 0000000..b5b9e0e --- /dev/null +++ b/src/Didot.Core/TemplateEngines/FluidWrapper.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Fluid; +using SmartFormat.Core.Extensions; + +namespace Didot.Core.TemplateEngines; +public class FluidWrapper : ITemplateEngine +{ + private static readonly FluidParser Parser = new(); + + public string Render(string source, object model) + { + var template = Parser.Parse(source); + var context = new TemplateContext(model); + return template.Render(context); + } + + public string Render(Stream stream, object model) + { + using var reader = new StreamReader(stream); + var template = reader.ReadToEnd(); + return Render(template, model); + } +} + diff --git a/testing/Didot.Cli.Testing/Didot.Cli.Testing.csproj b/testing/Didot.Cli.Testing/Didot.Cli.Testing.csproj index a72f245..74f920a 100644 --- a/testing/Didot.Cli.Testing/Didot.Cli.Testing.csproj +++ b/testing/Didot.Cli.Testing/Didot.Cli.Testing.csproj @@ -42,6 +42,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/testing/Didot.Cli.Testing/ProgramTests.cs b/testing/Didot.Cli.Testing/ProgramTests.cs index 5669275..c5ba201 100644 --- a/testing/Didot.Cli.Testing/ProgramTests.cs +++ b/testing/Didot.Cli.Testing/ProgramTests.cs @@ -5,7 +5,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Didot.Core.TemplateEngines; +using HandlebarsDotNet; using NUnit.Framework; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace Didot.Cli.Testing; @@ -115,6 +118,110 @@ public void Main_MissingTemplate_Failure() Assert.That(consoleOutput, Does.StartWith("Error parsing arguments.")); } } + + [Test] + public void Main_ForcedEngine_Success() + { + var args = new string[] + { + $"-ttemplate/template-01.liquid", + $"-sdata/data-01.json", + $"-efluid" + }; + Program.Main(args); + + MemoryStream.Position = 0; + using (var reader = new StreamReader(MemoryStream)) + { + var consoleOutput = reader.ReadToEnd().Standardize(); + var expected = File.ReadAllText(Path.Combine("Expectation", $"expectation-01.txt")).Standardize(); + Assert.That(consoleOutput, Is.EqualTo(expected)); + } + } + + [Test] + [TestCase("-x", ':')] + [TestCase("-x ", ':')] + [TestCase("--Extension=", ':')] + public void Main_AddNewExtension_Success(string token, char delimiter) + { + string extension = "txt"; + string engineTag = "handlebars"; + using var source = new StreamReader(Path.Combine("data", $"data-01.json")); + Console.SetIn(source); + + var args = new string[] + { + $"-ttemplate/template-01.{extension}", + $"{token}{extension}{delimiter}{engineTag}", + $"-pjson" + }; + Program.Main(args); + + MemoryStream.Position = 0; + using (var reader = new StreamReader(MemoryStream)) + { + var consoleOutput = reader.ReadToEnd().Standardize(); + var expected = File.ReadAllText(Path.Combine("Expectation", $"expectation-01.txt")).Standardize(); + Assert.That(consoleOutput, Is.EqualTo(expected)); + } + } + + [Test] + [TestCase("liquid", "fluid")] + public void Main_ReplaceExistingExtension_Success(string extension, string engineTag) + { + using var source = new StreamReader(Path.Combine("data", $"data-01.json")); + Console.SetIn(source); + + var args = new string[] + { + $"-ttemplate/template-01.{extension}", + $"-x{extension}:{engineTag}", + $"-pjson" + }; + Program.Main(args); + + MemoryStream.Position = 0; + using (var reader = new StreamReader(MemoryStream)) + { + var consoleOutput = reader.ReadToEnd().Standardize(); + var expected = File.ReadAllText(Path.Combine("Expectation", $"expectation-01.txt")).Standardize(); + Assert.That(consoleOutput, Is.EqualTo(expected)); + } + } + + [Test] + [TestCase("-x", ':', ';')] + [TestCase("-x ", ':', ';')] + [TestCase("--Extension=", ':', ';')] + public void Main_AddAndReplaceExtensions_Success(string token, char delimiter, char separator) + { + var extensions = new string[] { "txt", "liquid" }; + var engineTags = new string[] { "handlebars", "fluid" }; + using var source = new StreamReader(Path.Combine("data", $"data-01.json")); + Console.SetIn(source); + + var extensionArgs = $"{token}"; + for (int i = 0; i < extensions.Length; i++) + extensionArgs += $"{extensions[i]}{delimiter}{engineTags[i]}{separator}"; + + var args = new string[] + { + $"-ttemplate/template-01.{extensions[0]}", + extensionArgs, + $"-pjson" + }; + Program.Main(args); + + MemoryStream.Position = 0; + using (var reader = new StreamReader(MemoryStream)) + { + var consoleOutput = reader.ReadToEnd().Standardize(); + var expected = File.ReadAllText(Path.Combine("Expectation", $"expectation-01.txt")).Standardize(); + Assert.That(consoleOutput, Is.EqualTo(expected)); + } + } } public static class StringExtensions diff --git a/testing/Didot.Cli.Testing/Template/template-01.liquid b/testing/Didot.Cli.Testing/Template/template-01.liquid index 1993e05..66d6b18 100644 --- a/testing/Didot.Cli.Testing/Template/template-01.liquid +++ b/testing/Didot.Cli.Testing/Template/template-01.liquid @@ -1,3 +1,3 @@ {% for employee in model -%} {{ employee.name.first }} {{ employee.name.last }} is {{ employee.age }} years old. -{%- endfor %} +{% endfor %} diff --git a/testing/Didot.Cli.Testing/Template/template-01.txt b/testing/Didot.Cli.Testing/Template/template-01.txt new file mode 100644 index 0000000..e02a621 --- /dev/null +++ b/testing/Didot.Cli.Testing/Template/template-01.txt @@ -0,0 +1,3 @@ +{{#each model}} +{{this.name.first}} {{this.name.last}} is {{this.age}} years old. +{{/each}} diff --git a/testing/Didot.Core.Testing/TemplateEngines/DotLiquidWrapperTests.cs b/testing/Didot.Core.Testing/TemplateEngines/DotLiquidWrapperTests.cs index c6079c0..8648a92 100644 --- a/testing/Didot.Core.Testing/TemplateEngines/DotLiquidWrapperTests.cs +++ b/testing/Didot.Core.Testing/TemplateEngines/DotLiquidWrapperTests.cs @@ -10,10 +10,13 @@ namespace Didot.Core.Testing.TemplateEngines; public class DotLiquidWrapperTests { + protected virtual ITemplateEngine GetEngine() + => new DotLiquidWrapper(); + [Test] public void Render_SingleProperty_Successful() { - var engine = new DotLiquidWrapper(); + var engine = GetEngine(); var model = new Dictionary() { { "Name", "World"} }; var result = engine.Render("Hello {{model.Name}}", new { model }); @@ -23,7 +26,7 @@ public void Render_SingleProperty_Successful() [Test] public void Render_MultiProperty_Successful() { - var engine = new DotLiquidWrapper(); + var engine = GetEngine(); var model = new Dictionary() { { "Name", "Albert"}, {"Age", 30 } }; var result = engine.Render("Hello {{model.Name}}. You're {{model.Age}} years old.", new { model }); @@ -33,7 +36,7 @@ public void Render_MultiProperty_Successful() [Test] public void Render_NestedProperties_Successful() { - var engine = new DotLiquidWrapper(); + var engine = GetEngine(); var name = new Dictionary() { { "First", "Albert"}, {"Last", "Einstein" } }; var model = new Dictionary() @@ -45,7 +48,7 @@ public void Render_NestedProperties_Successful() [Test] public void Render_ArrayItems_Successful() { - var engine = new DotLiquidWrapper(); + var engine = GetEngine(); var albert = new Dictionary() { { "Name", "Albert"}, {"Age", 30 } }; var nikola = new Dictionary() @@ -58,7 +61,7 @@ public void Render_ArrayItems_Successful() [Test] public void Render_ArrayLoop_Successful() { - var engine = new DotLiquidWrapper(); + var engine = GetEngine(); var albert = new Dictionary() { { "Name", "Albert"}, {"Age", 30 } }; var nikola = new Dictionary() @@ -71,7 +74,7 @@ public void Render_ArrayLoop_Successful() [Test] public void Render_Stream_Successful() { - var engine = new DotLiquidWrapper(); + var engine = GetEngine(); var model = new Dictionary() { { "Name", "World"} }; using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Hello {{model.Name}}")); diff --git a/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs b/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs index d07b017..4677fb9 100644 --- a/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs +++ b/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs @@ -19,7 +19,7 @@ public class FileBasedTemplateEngineFactoryTests public void GetSourceParser_Extension_CorrectParser(string extension, Type expected) { var factory = new FileBasedTemplateEngineFactory(); - var parser = factory.GetTemplateEngine(extension); + var parser = factory.GetTemplateEngineByExtension(extension); Assert.That(parser, Is.TypeOf(expected)); } @@ -28,6 +28,29 @@ public void GetSourceParser_Extension_CorrectParser(string extension, Type expec public void GetSourceParser_NotSupportedExtension_Exception(string extension) { var factory = new FileBasedTemplateEngineFactory(); - Assert.Throws(() => factory.GetTemplateEngine(extension)); + Assert.Throws(() => factory.GetTemplateEngineByExtension(extension)); + } + + [Test] + public void GetSourceParser_AddedSourceParser_Exception() + { + var factory = new FileBasedTemplateEngineFactory(); + factory.AddOrReplaceEngine(".txt", new HandlebarsWrapper()); + var parser = factory.GetTemplateEngineByExtension(".txt"); + Assert.That(parser, Is.TypeOf()); + } + + [Test] + [TestCase("scriban", typeof(ScribanWrapper))] + [TestCase("dotliquid", typeof(DotLiquidWrapper))] + [TestCase("handlebars", typeof(HandlebarsWrapper))] + [TestCase("smartformat", typeof(SmartFormatWrapper))] + [TestCase("stringtemplate", typeof(StringTemplateWrapper))] + [TestCase("fluid", typeof(FluidWrapper))] + public void GetSourceParser_ByTag_Successful(string tag, Type expected) + { + var factory = new FileBasedTemplateEngineFactory(); + var parser = factory.GetTemplateEngineByTag(tag); + Assert.That(parser, Is.TypeOf(expected)); } } diff --git a/testing/Didot.Core.Testing/TemplateEngines/FluidWrapperTests.cs b/testing/Didot.Core.Testing/TemplateEngines/FluidWrapperTests.cs new file mode 100644 index 0000000..c968907 --- /dev/null +++ b/testing/Didot.Core.Testing/TemplateEngines/FluidWrapperTests.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Didot.Core.TemplateEngines; +using NUnit.Framework; + +namespace Didot.Core.Testing.TemplateEngines; +public class FluidWrapperTests : DotLiquidWrapperTests +{ + protected override ITemplateEngine GetEngine() + => new FluidWrapper(); +}