Skip to content

Commit

Permalink
feat: SmartFormat.Net template engine (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
Seddryck authored Oct 19, 2024
1 parent cfc80b3 commit bda4a11
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 6 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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:

Expand Down
3 changes: 2 additions & 1 deletion src/Didot.Core/Didot.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
<ItemGroup>
<PackageReference Include="DotLiquid" Version="2.2.692" />
<PackageReference Include="Handlebars.Net" Version="2.1.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Scriban" Version="5.10.0" />
<PackageReference Include="SmartFormat.NET" Version="3.5.1" />
<PackageReference Include="YamlDotNet" Version="16.1.3" />
</ItemGroup>

Expand Down
4 changes: 1 addition & 3 deletions src/Didot.Core/TemplateEngines/DotLiquidWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public ITemplateEngine GetTemplateEngine(string extension)
".scriban" => new ScribanWrapper(),
".liquid" => new DotLiquidWrapper(),
".hbs" => new HandlebarsWrapper(),
".smart" => new SmartFormatWrapper(),
_ => throw new NotSupportedException()
};
}
21 changes: 21 additions & 0 deletions src/Didot.Core/TemplateEngines/SmartFormatWrapper.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<string, object>()
{ { "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<string, object>()
{ { "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<string, object>()
{ { "First", "Albert"}, {"Last", "Einstein" } };
var model = new Dictionary<string, object>()
{ { "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<string, object>()
{ { "Name", "Albert"}, {"Age", 30 } };
var nikola = new Dictionary<string, object>()
{ { "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<string, object>()
{ { "Name", "Albert"}, {"Age", 30 } };
var nikola = new Dictionary<string, object>()
{ { "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<string, object>()
{ { "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"));
}
}

0 comments on commit bda4a11

Please sign in to comment.