diff --git a/README.md b/README.md index c176946..2fecdcc 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ dotnet tool install -g Didot-cli ## QuickStart -**Didot** is a command-line tool designed for generating files based on templating. It supports *YAML*, *JSON*, and *XML* as source data formats and provides flexibility in templating through both *Scriban*, *Liquid* and *Handlebars* templates languages. With Didot, you can easily automate file generation by combining structured data from YAML, JSON, or XML files with customizable templates using Scriban or DotLiquid. +**Didot** is a command-line tool designed for generating files based on templating. It supports *YAML*, *JSON*, and *XML* as source data formats and provides flexibility in templating through both *Scriban*, *Liquid*, *Handlebars* and *SmartFormat* templates languages. With Didot, you can easily automate file generation by combining structured data from YAML, JSON, or XML files with customizable templates using Scriban or DotLiquid. ### Supported Data Formats: @@ -55,14 +55,21 @@ Didot utilizes some templating engines, which allow for powerful and flexible te - Highly performant, designed to handle large-scale template processing. - Supports customizable scripting with rich expressions and filters. - Can work with JSON and YAML data sources. -- **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. + - 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. - 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. - **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. - Good separation of logic from presentation. + - Typical Use Case: Email templates, reports, and content generation. +- **SmartFormat**: Templates with the `.smart` extension are parsed using a SmartFormat template engine. SmartFormat.Net is a A lightweight templating engine primarily used for string formatting. + - Provides more advanced formatting capabilities than standard string formatting in C#. + - Supports nested templates, conditional formatting, and more. + - Typical Use Case: Log messages, report generation, and dynamic text formatting. ### Command Usage: diff --git a/src/Didot.Core/Didot.Core.csproj b/src/Didot.Core/Didot.Core.csproj index e29ef6b..d79afca 100644 --- a/src/Didot.Core/Didot.Core.csproj +++ b/src/Didot.Core/Didot.Core.csproj @@ -8,8 +8,9 @@ - + + diff --git a/src/Didot.Core/TemplateEngines/DotLiquidWrapper.cs b/src/Didot.Core/TemplateEngines/DotLiquidWrapper.cs index ce15188..fc11016 100644 --- a/src/Didot.Core/TemplateEngines/DotLiquidWrapper.cs +++ b/src/Didot.Core/TemplateEngines/DotLiquidWrapper.cs @@ -20,8 +20,6 @@ public string Render(Stream stream, dynamic model) { using var reader = new StreamReader(stream); var template = reader.ReadToEnd(); - var templateInstance = Template.Parse(template); - var hash = Hash.FromAnonymousObject(model); - return templateInstance.Render(hash); + return Render(template, model); } } diff --git a/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs b/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs index 868281e..b6162f9 100644 --- a/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs +++ b/src/Didot.Core/TemplateEngines/FileBasedTemplateEngineFactory.cs @@ -14,6 +14,7 @@ public ITemplateEngine GetTemplateEngine(string extension) ".scriban" => new ScribanWrapper(), ".liquid" => new DotLiquidWrapper(), ".hbs" => new HandlebarsWrapper(), + ".smart" => new SmartFormatWrapper(), _ => throw new NotSupportedException() }; } diff --git a/src/Didot.Core/TemplateEngines/SmartFormatWrapper.cs b/src/Didot.Core/TemplateEngines/SmartFormatWrapper.cs new file mode 100644 index 0000000..c82de8b --- /dev/null +++ b/src/Didot.Core/TemplateEngines/SmartFormatWrapper.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SmartFormat; + +namespace Didot.Core.TemplateEngines; +public class SmartFormatWrapper : ITemplateEngine +{ + public string Render(string template, dynamic model) + => Smart.Format(template, model); + + public string Render(Stream stream, dynamic model) + { + using var reader = new StreamReader(stream); + var template = reader.ReadToEnd(); + return Render(template, model); + } +} diff --git a/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs b/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs index 7412cdc..642b540 100644 --- a/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs +++ b/testing/Didot.Core.Testing/TemplateEngines/FileBasedTemplateEngineFactoryTests.cs @@ -13,6 +13,7 @@ public class FileBasedTemplateEngineFactoryTests [TestCase(".scriban", typeof(ScribanWrapper))] [TestCase(".liquid", typeof(DotLiquidWrapper))] [TestCase(".hbs", typeof(HandlebarsWrapper))] + [TestCase(".smart", typeof(SmartFormatWrapper))] public void GetSourceParser_Extension_CorrectParser(string extension, Type expected) { var factory = new FileBasedTemplateEngineFactory(); diff --git a/testing/Didot.Core.Testing/TemplateEngines/SmartFormatWrapperTests.cs b/testing/Didot.Core.Testing/TemplateEngines/SmartFormatWrapperTests.cs new file mode 100644 index 0000000..dd55c9e --- /dev/null +++ b/testing/Didot.Core.Testing/TemplateEngines/SmartFormatWrapperTests.cs @@ -0,0 +1,81 @@ +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 SmartFormatWrapperTests +{ + [Test] + public void Render_SingleProperty_Successful() + { + var engine = new SmartFormatWrapper(); + var model = new Dictionary() + { { "Name", "World"} }; + var result = engine.Render("Hello {model.Name}", new { model }); + Assert.That(result, Is.EqualTo("Hello World")); + } + + [Test] + public void Render_MultiProperty_Successful() + { + var engine = new SmartFormatWrapper(); + var model = new Dictionary() + { { "Name", "Albert"}, {"Age", 30 } }; + var result = engine.Render("Hello {model.Name}. You're {model.Age} years old.", new { model }); + Assert.That(result, Is.EqualTo("Hello Albert. You're 30 years old.")); + } + + [Test] + public void Render_NestedProperties_Successful() + { + var engine = new SmartFormatWrapper(); + var name = new Dictionary() + { { "First", "Albert"}, {"Last", "Einstein" } }; + var model = new Dictionary() + { { "Name", name}, {"Age", 30 } }; + var result = engine.Render("Hello {model.Name.First} {model.Name.Last}. Your age is {model.Age} years old.", new { model }); + Assert.That(result, Is.EqualTo("Hello Albert Einstein. Your age is 30 years old.")); + } + + [Test] + public void Render_ArrayItems_Successful() + { + var engine = new SmartFormatWrapper(); + var albert = new Dictionary() + { { "Name", "Albert"}, {"Age", 30 } }; + var nikola = new Dictionary() + { { "Name", "Nikola"}, {"Age", 50 } }; + var model = new[] { albert, nikola }; + var result = engine.Render("Hello {model[0].Name}. Your colleague is {model[1].Age} years old.", new { model }); + Assert.That(result, Is.EqualTo("Hello Albert. Your colleague is 50 years old.")); + } + + [Test] + public void Render_ArrayLoop_Successful() + { + var engine = new SmartFormatWrapper(); + var albert = new Dictionary() + { { "Name", "Albert"}, {"Age", 30 } }; + var nikola = new Dictionary() + { { "Name", "Nikola"}, {"Age", 50 } }; + var model = new[] { albert, nikola }; + var result = engine.Render("Hello {model:{Name}|, }!", new { model }); + Assert.That(result, Is.EqualTo("Hello Albert, Nikola!")); + } + + [Test] + public void Render_Stream_Successful() + { + var engine = new SmartFormatWrapper(); + var model = new Dictionary() + { { "Name", "World"} }; + using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Hello {model.Name}")); + var result = engine.Render(stream, new { model }); + Assert.That(result, Is.EqualTo("Hello World")); + } +}