< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Utilities.Yaml.PSObjectTypeConverter
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Utilities/Yaml/PSObjectTypeConverter.cs
Tag: Kestrun/Kestrun@2d87023b37eb91155071c91dd3d6a2eeb3004705
Line coverage
86%
Covered lines: 43
Uncovered lines: 7
Coverable lines: 50
Total lines: 143
Line coverage: 86%
Branch coverage
76%
Covered branches: 26
Total branches: 34
Branch coverage: 76.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 10/13/2025 - 16:52:37 Line coverage: 86% (43/50) Branch coverage: 76.4% (26/34) Total lines: 143 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e 10/13/2025 - 16:52:37 Line coverage: 86% (43/50) Branch coverage: 76.4% (26/34) Total lines: 143 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Accepts(...)100%11100%
ReadYaml(...)100%210%
WriteYaml(...)83.33%6683.33%
EmitNull(...)100%210%
IsDictionaryLike(...)50%4483.33%
SerializeNonDictionary(...)50%44100%
BeginMapping(...)100%22100%
EndMapping(...)100%11100%
WriteProperty(...)100%88100%
EmitNullProperty(...)100%11100%
UnwrapValue(...)70%111080%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Utilities/Yaml/PSObjectTypeConverter.cs

#LineLine coverage
 1using System.Collections;
 2using System.Management.Automation;
 3using YamlDotNet.Core;
 4using YamlDotNet.Core.Events;
 5using YamlDotNet.Serialization;
 6
 7namespace Kestrun.Utilities.Yaml;
 8
 9/// <summary>
 10/// YAML type converter for PSObject types
 11/// </summary>
 12/// <param name="omitNullValues">If true, null values will be omitted from the output</param>
 13/// <param name="useFlowStyle">If true, the mapping will be emitted in flow style</param>
 2814public class PSObjectTypeConverter(bool omitNullValues = false, bool useFlowStyle = false) : IYamlTypeConverter
 15{
 2816    private readonly bool omitNullValues = omitNullValues;
 2817    private readonly bool useFlowStyle = useFlowStyle;
 18
 19    /// <summary>
 20    /// Check if the type is PSObject
 21    /// </summary>
 22    /// <param name="type">The type to check</param>
 23    /// <returns>true if the type is PSObject; otherwise, false.</returns>
 15524    public bool Accepts(Type type) => typeof(PSObject).IsAssignableFrom(type);
 25
 26    /// <summary>
 27    /// Read a PSObject from YAML
 28    /// </summary>
 29    /// <param name="parser">The YAML parser</param>
 30    /// <param name="type">The target type</param>
 31    /// <param name="rootDeserializer">The root deserializer</param>
 32    /// <returns>The deserialized PSObject</returns>
 33    public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
 34    {
 35        // We don't really need to do any custom deserialization.
 036        var deserialized = rootDeserializer(typeof(IDictionary<string, object>)) as IDictionary;
 37        // Wrap the result in a PSObject so we never return null; if deserialized is null, the PSObject's BaseObject wil
 038        return new PSObject(deserialized);
 39    }
 40
 41    /// <summary>
 42    /// Write a PSObject to YAML
 43    /// </summary>
 44    /// <param name="emitter">The YAML emitter</param>
 45    /// <param name="value">The PSObject to serialize</param>
 46    /// <param name="type">The target type</param>
 47    /// <param name="serializer">The object serializer</param>
 48    public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
 49    {
 950        if (value is null)
 51        {
 052            EmitNull(serializer);
 053            return;
 54        }
 55
 956        var psObj = (PSObject)value;
 957        if (!IsDictionaryLike(psObj))
 58        {
 159            SerializeNonDictionary(psObj, serializer);
 160            return;
 61        }
 62
 863        BeginMapping(emitter);
 4064        foreach (var prop in psObj.Properties)
 65        {
 1266            WriteProperty(emitter, prop, serializer);
 67        }
 868        EndMapping(emitter);
 869    }
 70
 71    // ----------------- Helper Methods -----------------
 072    private static void EmitNull(ObjectSerializer serializer) => serializer(null, typeof(object));
 73
 74    private static bool IsDictionaryLike(PSObject psObj)
 75    {
 976        var baseObj = psObj.BaseObject;
 977        if (baseObj is null)
 78        {
 079            return false;
 80        }
 81
 982        var t = baseObj.GetType();
 983        return typeof(IDictionary).IsAssignableFrom(t) ||
 984           psObj.TypeNames.Contains("System.Management.Automation.PSCustomObject");
 85    }
 86
 87    private static void SerializeNonDictionary(PSObject psObj, ObjectSerializer serializer)
 188        => serializer(psObj.BaseObject, psObj.BaseObject?.GetType() ?? typeof(object));
 89
 90    private void BeginMapping(IEmitter emitter)
 91    {
 892        var mappingStyle = useFlowStyle ? MappingStyle.Flow : MappingStyle.Block;
 893        emitter.Emit(new MappingStart(AnchorName.Empty, TagName.Empty, true, mappingStyle));
 894    }
 95
 896    private static void EndMapping(IEmitter emitter) => emitter.Emit(new MappingEnd());
 97
 98    private void WriteProperty(IEmitter emitter, PSPropertyInfo prop, ObjectSerializer serializer)
 99    {
 12100        if (prop.Value is null)
 101        {
 2102            if (omitNullValues)
 103            {
 1104                return; // skip entirely
 105            }
 1106            EmitNullProperty(emitter, prop, serializer);
 1107            return;
 108        }
 109
 110        // Emit key
 10111        serializer(prop.Name, prop.Name.GetType());
 112
 10113        var (val, type) = UnwrapValue(prop.Value);
 114
 10115        if (val is string s && s.Length == 0)
 116        {
 117            // Double-quoted explicit empty string
 1118            emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, string.Empty, ScalarStyle.DoubleQuoted, true, false
 1119            return;
 120        }
 121
 9122        serializer(val, type);
 9123    }
 124
 125    private void EmitNullProperty(IEmitter emitter, PSPropertyInfo prop, ObjectSerializer serializer)
 126    {
 1127        serializer(prop.Name, prop.Name.GetType());
 1128        emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, "null", ScalarStyle.Plain, true, false));
 1129    }
 130
 131    private static (object value, Type type) UnwrapValue(object raw)
 132    {
 10133        if (raw is PSObject nested)
 134        {
 1135            var nestedType = nested.BaseObject?.GetType();
 1136            if (nestedType != null && nestedType != typeof(PSCustomObject) && nested.BaseObject != null)
 137            {
 0138                return (nested.BaseObject, nestedType);
 139            }
 140        }
 10141        return (raw, raw.GetType());
 142    }
 143}