<commit_before>﻿using System.Text.Json;
using System.Text.Json.Serialization;
using GitIStage.Commands;

namespace GitIStage.Services;

internal sealed class KeyBindingService
{
    public IReadOnlyDictionary<string, IReadOnlyList<ConsoleKeyBinding>> GetUserKeyBindings()
    {
        var result = new Dictionary<string, IReadOnlyList<ConsoleKeyBinding>>();
        var userKeyBindings = LoadUserKeyBindings();
        if (userKeyBindings is null)
            return result;
       
        foreach (var (name, bindingData) in userKeyBindings)
        {
            if (bindingData?.KeyBindings is not { Length: > 0 })
                continue;

            var bindings = new List<ConsoleKeyBinding>();

            if (bindingData.KeyBindings is not null)
            {
                foreach (var bindingText in bindingData.KeyBindings)
                {
                    if (bindingText is null)
                        continue;

                    if (!ConsoleKeyBinding.TryParse(bindingText, out var binding))
                    {
                        Console.WriteLine($"fatal: invalid key binding for '{name}': {binding}");
                        Environment.Exit(1);
                    }

                    bindings.Add(binding);
                }
            }

            result.Add(name, bindings.ToArray());
        }

        return result;
    }

    private static string UserKeyBindingsPath()
    {
        var homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        return Path.Join(homeDirectory, ".git-istage", "key-bindings.json");
    }

    private static IDictionary<string, CustomKeyBinding?>? LoadUserKeyBindings()
    {
        var path = UserKeyBindingsPath();
        if (string.IsNullOrEmpty(path) || !File.Exists(path))
            return null;
        
        var content = File.ReadAllText(path);
        return JsonSerializer.Deserialize<Dictionary<string, CustomKeyBinding?>>(content);
    }

    private sealed class CustomKeyBinding
    {
        // set to an empty array (or null) to clear the default bindings
        // for the corresponding command

        [JsonPropertyName("default")]
        public string?[]? Default { get; set; }

        [JsonPropertyName("keyBindings")]
        public string?[]? KeyBindings { get; set; }
    }
}<commit_msg>Improve handling of JSON<commit_after>﻿using System.Text.Json;
using System.Text.Json.Serialization;
using GitIStage.Commands;

namespace GitIStage.Services;

internal sealed class KeyBindingService
{
    public IReadOnlyDictionary<string, IReadOnlyList<ConsoleKeyBinding>> GetUserKeyBindings()
    {
        var result = new Dictionary<string, IReadOnlyList<ConsoleKeyBinding>>();
        var userKeyBindings = LoadUserKeyBindings();
        if (userKeyBindings is null)
            return result;
       
        foreach (var (name, bindingData) in userKeyBindings)
        {
            if (bindingData?.KeyBindings is not { Length: > 0 })
                continue;

            var bindings = new List<ConsoleKeyBinding>();

            if (bindingData.KeyBindings is not null)
            {
                foreach (var bindingText in bindingData.KeyBindings)
                {
                    if (bindingText is null)
                        continue;

                    if (!ConsoleKeyBinding.TryParse(bindingText, out var binding))
                    {
                        Console.WriteLine($"fatal: invalid key binding for '{name}': {binding}");
                        Environment.Exit(1);
                    }

                    bindings.Add(binding);
                }
            }

            result.Add(name, bindings.ToArray());
        }

        return result;
    }

    private static string UserKeyBindingsPath()
    {
        var homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        return Path.Join(homeDirectory, ".git-istage", "key-bindings.json");
    }

    private static IDictionary<string, CustomKeyBinding?>? LoadUserKeyBindings()
    {
        var path = UserKeyBindingsPath();
        if (string.IsNullOrEmpty(path) || !File.Exists(path))
            return null;
        
        var content = File.ReadAllText(path);
        var options = new JsonSerializerOptions(JsonSerializerDefaults.General)
        {
            AllowTrailingCommas = true,
            ReadCommentHandling = JsonCommentHandling.Skip
        };

        try
        {
            return JsonSerializer.Deserialize<Dictionary<string, CustomKeyBinding?>>(content, options);
        }
        catch (JsonException ex)
        {
            Console.WriteLine($"fatal: user key bindings in {path} are malformed: {ex.Message}");
            Environment.Exit(1);
            throw;
        }
    }

    private sealed class CustomKeyBinding
    {
        [JsonPropertyName("keyBindings")]
        public string?[]? KeyBindings { get; set; }
    }
}
{'fraction_non_alphanumeric': 0.09206798866855524, 'fraction_numerical': 0.0010117361392148927, 'mean_word_length': 1.9903206291591047, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5704505', 'n_tokens_mistral': 1268, 'n_tokens_neox': 1222, 'n_words': 289}