|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.EventEmitters;
namespace Aspire.Hosting.Yaml;
/// <summary>
/// A custom event emitter that forces string scalar values to use double-quoted style when serialized in YAML.
/// </summary>
/// <remarks>
/// This class extends the <see cref="ChainedEventEmitter"/> to modify the behavior of how scalar values are emitted.
/// It ensures that all string values are wrapped in double quotes, which can be useful for preserving string formatting
/// or compatibility with specific YAML consumers.
/// </remarks>
internal sealed class ForceQuotedStringsEventEmitter : ChainedEventEmitter
{
private readonly Stack<EmitterState> _state = new();
/// <summary>
/// A custom event emitter that forces string scalar values to use double-quoted style when serialized in YAML.
/// </summary>
/// <remarks>
/// This class extends the <see cref="ChainedEventEmitter"/> to modify the behavior of how scalar values are emitted.
/// It ensures that all string values are wrapped in double quotes, which can be useful for preserving string formatting
/// or compatibility with specific YAML consumers.
/// </remarks>
public ForceQuotedStringsEventEmitter(
IEventEmitter nextEmitter
) : base(nextEmitter)
{
_state.Push(new(EmitterState.EventType.Root));
}
/// <summary>
/// Emits a scalar event in YAML serialization, with customization to enforce double-quoted style
/// for string scalar values when applicable.
/// </summary>
/// <param name="eventInfo">
/// Metadata about the scalar value being emitted, such as its type and style details.
/// </param>
/// <param name="emitter">
/// The emitter responsible for outputting the scalar event to the YAML stream.
/// </param>
public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
{
var item = _state.Peek();
item.Move();
if (item.ShouldApply() && eventInfo.Source.Type == typeof(string))
{
eventInfo = new(eventInfo.Source)
{
Style = ScalarStyle.DoubleQuoted,
};
}
base.Emit(eventInfo, emitter);
}
/// <summary>
/// Emits a YAML scalar event while ensuring string scalar values are double-quoted if applicable.
/// </summary>
/// <param name="eventInfo">
/// An instance of <see cref="ScalarEventInfo"/> that contains information about the scalar event.
/// </param>
/// <param name="emitter">
/// An instance of <see cref="IEmitter"/> used to write the serialized YAML data.
/// </param>
public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter)
{
_state.Peek().Move();
_state.Push(new(EmitterState.EventType.Mapping));
base.Emit(eventInfo, emitter);
}
/// <summary>
/// Emits a YAML scalar event, forcing string scalar values to use double-quoted style when necessary.
/// </summary>
/// <param name="eventInfo">
/// Information about the scalar event being emitted, including the source value and its metadata.
/// </param>
/// <param name="emitter">
/// The emitter used to write the YAML events.
/// </param>
/// <remarks>
/// This method checks the current state of the emitter and applies double-quoted style
/// to string scalar events if required by the emitter's state logic.
/// It overrides the functionality of the base event emitter to enforce custom serialization rules.
/// </remarks>
public override void Emit(MappingEndEventInfo eventInfo, IEmitter emitter)
{
var item = _state.Pop();
if (item.Type != EmitterState.EventType.Mapping)
{
throw new YamlException("Invalid state: expected MappingEndEventInfo.");
}
base.Emit(eventInfo, emitter);
}
/// <summary>
/// Forces string scalar values to use double-quoted style when serialized in YAML.
/// </summary>
/// <param name="eventInfo">The scalar event information to be emitted, containing details about the value and its metadata.</param>
/// <param name="emitter">The YAML emitter responsible for handling the event and producing output.</param>
public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter)
{
_state.Peek().Move();
_state.Push(new(EmitterState.EventType.Sequence));
base.Emit(eventInfo, emitter);
}
/// <summary>
/// Emits a YAML event with support for forcing string scalar values into double-quoted style when applicable.
/// </summary>
/// <param name="eventInfo">The metadata of the scalar event to be emitted, including type and style information.</param>
/// <param name="emitter">The emitter responsible for writing the event to the YAML output.</param>
public override void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter)
{
var item = _state.Pop();
if (item.Type != EmitterState.EventType.Sequence)
{
throw new YamlException("Invalid state: expected SequenceEndEventInfo.");
}
base.Emit(eventInfo, emitter);
}
private sealed class EmitterState(
EmitterState.EventType eventType
)
{
public EventType Type { get; } = eventType;
private int _currentIndex;
public void Move()
{
_currentIndex++;
}
public bool ShouldApply() => Type switch
{
EventType.Mapping => _currentIndex % 2 == 0,
EventType.Sequence => true,
_ => false,
};
public enum EventType : byte
{
Root,
Mapping,
Sequence,
}
}
}
|