< Summary

Information
Class: Allyaria.Theming.Helpers.CssBuilder
Assembly: Allyaria.Theming
File(s): /home/runner/work/allyaria/allyaria/src/Allyaria.Theming/Helpers/CssBuilder.cs
Line coverage
100%
Covered lines: 30
Uncovered lines: 0
Coverable lines: 30
Total lines: 136
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
Add(...)100%88100%
AddRange(...)100%66100%
ToString()100%44100%

File(s)

/home/runner/work/allyaria/allyaria/src/Allyaria.Theming/Helpers/CssBuilder.cs

#LineLine coverage
 1namespace Allyaria.Theming.Helpers;
 2
 3/// <summary>
 4/// Provides a fluent, immutable-safe builder for constructing CSS property/value declarations used throughout the Allya
 5/// theming system.
 6/// <para>
 7/// <see cref="CssBuilder" /> is designed to aggregate style key-value pairs in a deterministic order using a
 8/// <see cref="SortedDictionary{TKey,TValue}" /> for consistent and predictable CSS output.
 9/// </para>
 10/// </summary>
 11/// <remarks>
 12/// This class is central to how Allyaria generates serialized CSS from the strongly-typed <see cref="IStyleValue" /> an
 13/// <see cref="StyleGroup" /> abstractions.
 14/// </remarks>
 15public sealed class CssBuilder
 16{
 17    /// <summary>
 18    /// The underlying collection of CSS property/value pairs. The use of <see cref="SortedDictionary{TKey,TValue}" /> e
 19    /// consistent ordering of CSS properties in the final output string, improving diffability and test determinism.
 20    /// </summary>
 11621    private readonly SortedDictionary<string, string> _styles = new();
 22
 23    /// <summary>
 24    /// Adds a new CSS property/value pair to the builder, if both are valid and non-empty.
 25    /// <para>
 26    /// Property names and prefixes are automatically converted to CSS-compliant formats using the <c>ToCssName()</c> he
 27    /// (e.g., PascalCase → kebab-case).
 28    /// </para>
 29    /// </summary>
 30    /// <param name="name">The CSS property name or token name to add. This will be normalized to kebab-case.</param>
 31    /// <param name="value">
 32    /// The CSS value associated with the property. Must be non-empty and already validated by the corresponding
 33    /// <see cref="IStyleValue" /> implementation.
 34    /// </param>
 35    /// <param name="varPrefix">
 36    /// An optional variable prefix used to generate CSS custom properties (e.g., <c>--theme-color-primary</c>). If prov
 37    /// the final property name becomes <c>--{prefix}-{property}</c>.
 38    /// </param>
 39    /// <returns>The current <see cref="CssBuilder" /> instance, enabling fluent method chaining.</returns>
 40    /// <remarks>
 41    /// If the property or value is empty or invalid, the call is ignored and the builder remains unchanged.
 42    /// </remarks>
 43    public CssBuilder Add(string? name, string? value, string? varPrefix = "")
 44    {
 8845        if (string.IsNullOrWhiteSpace(value: name) || string.IsNullOrWhiteSpace(value: value))
 46        {
 647            return this;
 48        }
 49
 50        string property;
 51
 52        try
 53        {
 8254            property = name.FromPrefixedCase().ToCssName();
 8155        }
 156        catch
 57        {
 158            property = string.Empty;
 159        }
 60
 8261        if (string.IsNullOrWhiteSpace(value: property))
 62        {
 163            return this;
 64        }
 65
 8166        var prefix = varPrefix.ToCssName();
 67
 8168        var propertyName = string.IsNullOrWhiteSpace(value: prefix)
 8169            ? property
 8170            : $"--{prefix}-{property}";
 71
 8172        _ = _styles.TryAdd(key: propertyName, value: value);
 73
 8174        return this;
 75    }
 76
 77    /// <summary>Adds multiple CSS property/value pairs to the builder by parsing a semicolon-delimited list.</summary>
 78    /// <param name="cssList">
 79    /// A semicolon-separated list of CSS declarations in the form <c>property:value</c>. Entries with missing property 
 80    /// value segments are ignored.
 81    /// </param>
 82    /// <returns>The current <see cref="CssBuilder" /> instance, enabling fluent method chaining.</returns>
 83    /// <remarks>
 84    /// This method provides a convenience parser for raw CSS fragments and forwards each parsed entry to
 85    /// <see cref="Add(string?, string?, string?)" />. Invalid entries are safely skipped.
 86    /// </remarks>
 87    public CssBuilder AddRange(string? cssList)
 88    {
 1089        if (string.IsNullOrWhiteSpace(value: cssList))
 90        {
 391            return this;
 92        }
 93
 794        var split = cssList.Split(separator: ';', options: StringSplitOptions.RemoveEmptyEntries);
 95
 4096        foreach (var item in split)
 97        {
 1398            var pair = item.Split(separator: ':', options: StringSplitOptions.RemoveEmptyEntries);
 99
 13100            if (pair.Length < 2)
 101            {
 102                continue;
 103            }
 104
 11105            Add(name: pair[0], value: pair[1]);
 106        }
 107
 7108        return this;
 109    }
 110
 111    /// <summary>
 112    /// Builds the final CSS representation of all accumulated styles in this builder.
 113    /// <para>
 114    /// The result is a semicolon-separated list of <c>property:value</c> pairs, ready for inline or stylesheet inclusio
 115    /// </para>
 116    /// </summary>
 117    /// <returns>
 118    /// A single CSS string containing all property/value pairs. If no styles are defined, returns <see cref="string.Emp
 119    /// </returns>
 120    public override string ToString()
 121    {
 116122        if (_styles.Count is 0)
 123        {
 68124            return string.Empty;
 125        }
 126
 48127        var list = new List<string>();
 128
 254129        foreach (var styleValue in _styles)
 130        {
 79131            list.Add(item: $"{styleValue.Key}:{styleValue.Value}");
 132        }
 133
 48134        return string.Join(separator: ';', values: list) + ";";
 135    }
 136}