File: JExtensions.cs
Web Access
Project: src\src\sdk\src\Cli\Microsoft.TemplateEngine.Cli\Microsoft.TemplateEngine.Cli.csproj (Microsoft.TemplateEngine.Cli)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#if !NET6_0_OR_GREATER
using System;
using System.Collections.Generic;
using System.IO;
#endif
using Microsoft.TemplateEngine.Abstractions.PhysicalFileSystem;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace Microsoft.TemplateEngine
{
    internal static class JExtensions
    {
        private static readonly JsonDocumentOptions DocOptions = new() { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true };

        internal static string? ToString(this JsonNode? token, string? key)
        {
            if (key == null)
            {
                if (token == null)
                {
                    return null;
                }

                if (token is JsonValue val && val.GetValueKind() == JsonValueKind.String)
                {
                    return val.GetValue<string>();
                }

                return null;
            }

            if (token is not JsonObject obj)
            {
                return null;
            }

            JsonNode? element = GetPropertyCaseInsensitive(obj, key);
            if (element == null || element.GetValueKind() != JsonValueKind.String)
            {
                return null;
            }

            return element.GetValue<string>();
        }

        internal static bool TryGetValue(this JsonNode? token, string? key, out JsonNode? result)
        {
            result = null;

            // determine which token to bool-ify
            if (token == null)
            {
                return false;
            }
            else if (key == null)
            {
                result = token;
            }
            else
            {
                result = GetPropertyCaseInsensitive(token.AsObject(), key);
                if (result == null)
                {
                    return false;
                }
            }

            return true;
        }

        internal static bool TryParseBool(this JsonNode token, out bool result)
        {
            result = false;
            var kind = token.GetValueKind();
            if (kind == JsonValueKind.True)
            {
                result = true;
                return true;
            }
            if (kind == JsonValueKind.False)
            {
                result = false;
                return true;
            }
            if (kind == JsonValueKind.String)
            {
                return bool.TryParse(token.GetValue<string>(), out result);
            }
            return false;
        }

        internal static bool ToBool(this JsonNode? token, string? key = null, bool defaultValue = false)
        {
            if (!token.TryGetValue(key, out JsonNode? checkToken))
            {
                return defaultValue;
            }

            if (!checkToken!.TryParseBool(out bool result))
            {
                result = defaultValue;
            }

            return result;
        }

        internal static int ToInt32(this JsonNode? token, string? key = null, int defaultValue = 0)
        {
            if (key == null)
            {
                if (token == null || !token.TryParseInt(out int value))
                {
                    return defaultValue;
                }

                return value;
            }

            if (token is not JsonObject obj)
            {
                return defaultValue;
            }

            JsonNode? element = GetPropertyCaseInsensitive(obj, key);
            if (element == null || !element.TryParseInt(out int result))
            {
                return defaultValue;
            }

            return result;
        }

        internal static T ToEnum<T>(this JsonNode token, string? key = null, T defaultValue = default)
            where T : struct
        {
            string? val = token.ToString(key);
            if (val == null || !Enum.TryParse(val, out T result))
            {
                return defaultValue;
            }

            return result;
        }

        internal static Guid ToGuid(this JsonNode token, string? key = null, Guid defaultValue = default)
        {
            string? val = token.ToString(key);
            if (val == null || !Guid.TryParse(val, out Guid result))
            {
                return defaultValue;
            }

            return result;
        }

        internal static IEnumerable<KeyValuePair<string, JsonNode?>> PropertiesOf(this JsonNode? token, string? key = null)
        {
            if (token is not JsonObject currentJObj)
            {
                return Array.Empty<KeyValuePair<string, JsonNode?>>();
            }

            if (key != null)
            {
                JsonNode? element = GetPropertyCaseInsensitive(currentJObj, key);
                if (element is not JsonObject nested)
                {
                    return Array.Empty<KeyValuePair<string, JsonNode?>>();
                }
                return nested.ToList();
            }

            return currentJObj.ToList();
        }

        internal static T? Get<T>(this JsonNode? token, string? key)
            where T : JsonNode
        {
            if (token is not JsonObject obj || key == null)
            {
                return default;
            }

            JsonNode? res = GetPropertyCaseInsensitive(obj, key);
            return res as T;
        }

        internal static IReadOnlyList<string> ArrayAsStrings(this JsonNode? token, string? propertyName = null)
        {
            if (propertyName != null)
            {
                token = token.Get<JsonArray>(propertyName);
            }

            if (token is not JsonArray arr)
            {
                return Array.Empty<string>();
            }

            List<string> values = new();

            foreach (JsonNode? item in arr)
            {
                if (item != null && item.GetValueKind() == JsonValueKind.String)
                {
                    values.Add(item.GetValue<string>());
                }
            }

            return values;
        }

        internal static JsonObject ReadObject(this IPhysicalFileSystem fileSystem, string path)
        {
            using Stream fileStream = fileSystem.OpenRead(path);
            using var textReader = new StreamReader(fileStream, Encoding.UTF8, true);
            string json = textReader.ReadToEnd();
            return (JsonObject?)JsonNode.Parse(json, null, DocOptions)
                ?? throw new InvalidOperationException($"Failed to parse JSON from '{path}'.");
        }

        internal static void WriteObject(this IPhysicalFileSystem fileSystem, string path, JsonNode obj)
        {
            using Stream fileStream = fileSystem.CreateFile(path);
            using var writer = new Utf8JsonWriter(fileStream);
            obj.WriteTo(writer);
        }

        internal static bool TryParse(this string arg, out JsonNode? token)
        {
            try
            {
                token = JsonNode.Parse(arg, null, DocOptions);
                return true;
            }
            catch
            {
                token = null;
                return false;
            }
        }

        private static bool TryParseInt(this JsonNode token, out int result)
        {
            result = default;
            var kind = token.GetValueKind();
            if (kind == JsonValueKind.Number)
            {
                if (token is JsonValue jv && jv.TryGetValue(out int intVal))
                {
                    result = intVal;
                    return true;
                }
                return int.TryParse(token.ToJsonString(), out result);
            }
            if (kind == JsonValueKind.String)
            {
                return int.TryParse(token.GetValue<string>(), out result);
            }
            return false;
        }

        private static JsonNode? GetPropertyCaseInsensitive(JsonObject obj, string key)
        {
            if (obj.TryGetPropertyValue(key, out JsonNode? result))
            {
                return result;
            }

            foreach (var kvp in obj)
            {
                if (string.Equals(kvp.Key, key, StringComparison.OrdinalIgnoreCase))
                {
                    return kvp.Value;
                }
            }

            return null;
        }
    }
}