// // PacketStringBuilder.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Text; using Remora.Results; namespace NosSmooth.PacketSerializer.Abstractions; /// /// String builder for packets. /// public class PacketStringBuilder { private readonly StringBuilder _builder; private StringBuilderLevel _currentLevel; private char? _insertSeparator; /// /// Initializes a new instance of the class. /// /// The top level separator. public PacketStringBuilder(char separator = ' ') { _currentLevel = new StringBuilderLevel(null, separator); _insertSeparator = null; _builder = new StringBuilder(); } /// /// Sets the separator to search for only once. /// /// /// This separator will have the most priority. /// /// The separator to look for. public void SetAfterSeparatorOnce(char separator) { _currentLevel.SeparatorOnce = separator; } /// /// Prepare the given level that can be set when needed. /// /// The separator of the prepared level. public void PrepareLevel(char separator) { _currentLevel.PreparedLevelSeparator = separator; } /// /// Reset the prepared level, if there is any. /// public void RemovePreparedLevel() { _currentLevel.PreparedLevelSeparator = null; } /// /// Create next level with the separator given in the prepared level. /// /// /// Level of the current builder will stay the same. /// Will return null, if there is not a level prepared. /// /// An enumerator with the new level pushed. public bool PushPreparedLevel() { if (_currentLevel.PreparedLevelSeparator is null) { return false; } _currentLevel = new StringBuilderLevel(_currentLevel, _currentLevel.PreparedLevelSeparator.Value); return true; } /// /// Push new separator level to the stack. /// /// /// This will change the current enumerator. /// It has to be after parent level should be used. /// /// The separator of the new level. public void PushLevel(char separator) { _currentLevel = new StringBuilderLevel(_currentLevel, separator); } /// /// Pop the current level. /// /// Whether to replace the last separator with the parent one. /// A result that may or may not have succeeded. There will be an error if the current level is the top one. public Result PopLevel(bool replaceSeparator = true) { if (_currentLevel.Parent is null) { return new InvalidOperationError("The level cannot be popped, the stack is already at the top level."); } if (replaceSeparator) { ReplaceWithParentSeparator(); } _currentLevel = _currentLevel.Parent; return Result.FromSuccess(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(string value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(int value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(uint value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(short value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(char value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(ushort value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(long value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(ulong value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(byte value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(sbyte value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(float value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(double value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } /// /// Appends the value to the string. /// /// The value to append. public void Append(decimal value) { BeforeAppend(); _builder.Append(value); AfterAppend(); } private void BeforeAppend() { if (_insertSeparator is not null) { _builder.Append(_insertSeparator); _insertSeparator = null; } } private void AfterAppend() { _insertSeparator = _currentLevel.SeparatorOnce ?? _currentLevel.Separator; _currentLevel.SeparatorOnce = null; } /// /// Replace the last separator with the parent separator. /// public void ReplaceWithParentSeparator() { var parent = _currentLevel.Parent; if (_insertSeparator is null || parent is null) { return; } _insertSeparator = parent.SeparatorOnce ?? parent.Separator; parent.SeparatorOnce = null; } /// public override string ToString() { return _builder.ToString(); } private class StringBuilderLevel { public StringBuilderLevel(StringBuilderLevel? parent, char separator) { Parent = parent; Separator = separator; } public StringBuilderLevel? Parent { get; } public char? PreparedLevelSeparator { get; set; } public char Separator { get; } public char? SeparatorOnce { get; set; } } }