StringEnricher 0.0.1-rc.0

This is a prerelease version of StringEnricher.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package StringEnricher --version 0.0.1-rc.0
                    
NuGet\Install-Package StringEnricher -Version 0.0.1-rc.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="StringEnricher" Version="0.0.1-rc.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="StringEnricher" Version="0.0.1-rc.0" />
                    
Directory.Packages.props
<PackageReference Include="StringEnricher" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add StringEnricher --version 0.0.1-rc.0
                    
#r "nuget: StringEnricher, 0.0.1-rc.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package StringEnricher@0.0.1-rc.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=StringEnricher&version=0.0.1-rc.0&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=StringEnricher&version=0.0.1-rc.0&prerelease
                    
Install as a Cake Tool

StringEnricher

Build Status NuGet Version NuGet Downloads License: MIT .NET Version GitHub Stars GitHub Issues GitHub Last Commit

StringEnricher is a powerful and extensible C# library for building and enriching strings with rich text styles, supporting formats such as HTML and MarkdownV2. It is designed for scenarios where you need to dynamically compose styled messages, such as chatbots, messaging apps, or document generators.

Features

  • High performance: Optimized for minimal allocations and fast execution.
  • Rich style system: Apply styles like bold, italic, underline, strikethrough, code blocks, blockquotes, spoilers, links, and more.
  • Multi-format support: Easily switch between HTML and MarkdownV2.
  • Composable styles: Nest and combine styles for complex formatting.
  • Well-tested: Comprehensive unit tests for all styles and formats.

Getting Started

Requirements

  • .NET 9.0 or later

Installation

Execute the following command in your project directory:

dotnet add package StringEnricher

Usage

Basic Example (HTML Bold)

using StringEnricher.Node.Html;

var styledBold = BoldHtml.Apply("bold text"); // 0 heap allocations here
var styledBoldString = styledBold.ToString(); // 1 final string heap allocation here
// styledBold == "<b>bold text</b>"

Applying Multiple Node

using StringEnricher.Node.Html;

var styled = BoldHtml.Apply(
    ItalicHtml.Apply("important text") // 0 heap allocations here
); // 0 heap allocations here
var styledString = styled.ToString(); // 1 final string heap allocation here
// styled == "<b><i>important text</i></b>"

MarkdownV2 Example

using StringEnricher.Node.MarkdownV2;

var boldMd = BoldMarkdownV2.Apply("bold text"); // 0 heap allocations here
var boldMdString = boldMd.ToString(); // 1 final string heap allocation here
// boldMd == "*bold text*"

The .CopyTo() method for Zero Allocations

using StringEnricher.Node.Html;

var styled = BoldHtml.Apply("bold text");
Span<char> buffer = stackalloc char[styled.GetMaxLength()];
int written = styled.CopyTo(buffer); // 0 heap allocations here
var result = new string(buffer.Slice(0, written)); // 1 final string heap
// result == "<b>bold text</b>"

Note: This approach is OK for small strings that fit on the stack (up to 1-2 KB). For larger strings, use ToString().

.ToString() method for Final String Creation

The ToString() method is used to create the final styled string. It performs a single heap allocation for the resulting string. Use it only when you finished building the entire styled string.

.TryGetChar() method for Single Character Access

The TryGetChar(int index, out char value) method allows you to access individual characters in the styled string without creating the entire string. It returns true if the character at the specified index exists, otherwise false.

using StringEnricher.Node.Html;
var styled = BoldHtml.Apply("bold text");
if (styled.TryGetChar(0, out char character))
{
    // character == '*'
}
if (styled.TryGetChar(11, out char character))
{
    // character == '*'
}
if (styled.TryGetChar(12, out char character))
{
    // this is out of bounds
}
else {
    // character == '\0'
}

.CombineWith() method for Merging Nodes

The CombineWith(INode other) method allows you to merge two nodes into a single node. This is useful for building complex styled strings from multiple parts.

using StringEnricher.Node.Html;
var part1 = BoldHtml.Apply("bold text");
var part2 = ItalicHtml.Apply(" and italic text");
var combined = part1.CombineWith(part2); // 0 heap allocations here
var combinedString = combined.ToString(); // 1 final string heap allocation here
// combinedString == "<b>bold text</b><i> and italic text</i

MessageBuilder for Fluent API

using StringEnricher;
using StringEnricher.Node.Html;
var messageBuilder = new MessageBuilder(totalLength);
var state = ["Hello, ", "World! ", "Every ", "word ", "is ", "in ", "different ", "style&"];
var string result = messageBuilder.Create(state, static (state, writer) => 
{
    writer.Append(BoldHtml.Apply(state[0])); // "*Hello, *" - 0 heap allocations here
    writer.Append(ItalicHtml.Apply(state[1])); // "_World! _" - 0 heap allocations here
    writer.Append(UnderlineHtml.Apply(state[2])); // "__Every __" - 0 heap allocations here
    writer.Append(StrikethroughHtml.Apply(state[3])); // "~word ~" - 0 heap allocations here
    writer.Append(CodeHtml.Apply(state[4])); // "`is`" - 0 heap allocations here
    writer.Append(BlockquoteHtml.Apply(state[5])); // "> different " - 0 heap allocations here
    writer.Append(SpoilerHtml.Apply(state[6])); // "||style||" - 0 heap allocations here
    writer.Append(EscapeHtml.Apply(state[7])); // "style&amp;" - 0 heap allocations here
}); // 1 final string allocated in heap without any intermediate allocations

Using Aliases for Node via GlobalUsings.cs

To simplify switching between HTML and MarkdownV2 styles across your project, you can use C# using aliases in a GlobalUsings.cs file. This allows you to reference style helpers (like Bold, Italic, etc.) generically, and change the underlying format by updating just one file.

Example: GlobalUsings.cs

// GlobalUsings.cs
// Place this file in your project root or any folder included in compilation.

global using Bold = StringEnricher.Node.Html.BoldHtml;
global using Italic = StringEnricher.Node.Html.ItalicHtml;
// Add other aliases as needed

To switch to MarkdownV2, simply update the aliases:

// GlobalUsings.cs

global using Bold = StringEnricher.Node.MarkdownV2.BoldMarkdown;
global using Italic = StringEnricher.Node.MarkdownV2.ItalicMarkdown;
// ...

Usage in Your Code

var styled = Bold.Apply(
    Italic.Apply("important text") // 0 heap allocations here
); // 0 heap allocations here
var styledString = styled.ToString(); // 1 final string heap allocation here
// styled == "<b><i>important text</i></b>" (HTML)
// styled == "* _important text_ *" (MarkdownV2)

This approach centralizes format selection, making it easy to switch formats for the entire project by editing only GlobalUsings.cs.

Notes

  • Prefer .CopyTo() for zero allocations.
  • Use .ToString() for final string creation.
  • .TryGetChar() for single character access.
  • .CombineWith() for merging nodes at a compile time.
  • MessageBuilder for fluent API and complex message construction at runtime.
  • Escape special characters using EscapeHtml or EscapeMarkdownV2 nodes.
    • Also, available as static methods: HtmlEscaper.Escape(string) and MarkdownV2Escaper.Escape(string). But these create intermediate strings.
  • It is recommended to use Html format for better performance and stability by format consumers (like TG) unless MarkdownV2 is specifically required.
    • Html by its nature is more robust and less error-prone than MarkdownV2.
    • MarkdownV2 has many edge cases and limitations that can lead to formatting issues.
    • Html-related code paths are generally faster and more memory efficient than MarkdownV2 paths.
  • Despite the fact that every node implements INode interface, avoid using INode directly in performance-critical paths to prevent boxing allocations. Use concrete node types instead.
    • The INode interface is used in this library only for generic definitions as a constraint. The library itself never uses INode directly as it will cause boxing/unboxing.
  • Check existing benchmarks in the benchmarks folder. You can run them using BenchmarkDotNet.
    • You can find interesting results there, including comparisons of different string building approaches.
    • I strongly recommend to review all benchmarks if you want to write the most performant code using this library. You will get understanding how the library works under the hood and how to use it in the most efficient way.
    • Also, I recommend to write your custom benchmarks for your specific use cases to check performance and memory allocations. Sometimes the most optimal approach is not obvious and depends on the specific scenario.

Project Structure

  • src/StringEnricher/: Core library
    • Nodes/: Nodes definitions for HTML, MarkdownV2, PlainText, etc.
  • tests/StringEnricher.Tests/: Unit tests for all styles and formats

Facts

  • Designed for high performance and composability
  • Easily extendable for new formats and styles
  • Suitable for chatbots, messaging apps, and document generation

License

MIT

Benchmarks

Benchmarks are available in the benchmarks folder. You can run them using BenchmarkDotNet.


Feel free to contribute or open issues for feature requests and bug reports!

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.1-rc.9 39 9/15/2025
0.0.1-rc.8 21 9/15/2025
0.0.1-rc.7 121 9/11/2025
0.0.1-rc.6 119 9/10/2025
0.0.1-rc.5 118 9/10/2025
0.0.1-rc.4 114 9/9/2025
0.0.1-rc.3 129 9/7/2025
0.0.1-rc.2 127 9/7/2025
0.0.1-rc.1 90 9/7/2025
0.0.1-rc.0 124 8/31/2025