Aragas/Bannerlord.MBOptionScreen

View on GitHub
src/MCM.Abstractions/Formats/BaseJsonSettingsFormat.cs

Summary

Maintainability
A
0 mins
Test Coverage
using BUTR.DependencyInjection;
using BUTR.DependencyInjection.Logger;

using MCM.Abstractions;
using MCM.Abstractions.Base;
using MCM.Abstractions.GameFeatures;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;

namespace MCM.Implementation
{
#if !BANNERLORDMCM_INCLUDE_IN_CODE_COVERAGE
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, global::System.Diagnostics.DebuggerNonUserCode]
#endif
#if !BANNERLORDMCM_PUBLIC
    internal
#else
    public
# endif
    abstract class BaseJsonSettingsFormat : ISettingsFormat
    {
        private readonly object _lock = new();
        protected readonly Dictionary<string, object?> _existingObjects = new();

        protected readonly IBUTRLogger Logger;
        protected virtual JsonSerializerSettings JsonSerializerSettings { get; }

        protected BaseJsonSettingsFormat(IBUTRLogger logger)
        {
            Logger = logger;
            JsonSerializerSettings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                Converters =
                {
                    new BaseSettingsJsonConverter(logger, AddSerializationProperty, ClearSerializationProperties),
                    new DropdownJsonConverter(logger, GetSerializationProperty),
                }
            };
        }

        public virtual IEnumerable<string> FormatTypes { get; } = new[] { "json" };

        public string SaveJson(BaseSettings settings)
        {
            return JsonConvert.SerializeObject(settings, JsonSerializerSettings);
        }

        public BaseSettings LoadFromJson(BaseSettings settings, string content)
        {
            TryLoadFromJson(ref settings, content);
            return settings;
        }
        protected bool TryLoadFromJson(ref BaseSettings settings, string content)
        {
            lock (_lock)
            {
                try
                {
                    if (!TryParse(content, out var jo))
                        return false;

                    using var reader = jo.CreateReader();
                    var serializer = JsonSerializer.CreateDefault(JsonSerializerSettings);
                    var settingsType = settings.GetType();
                    var settingsConverter = JsonSerializerSettings.Converters.OfType<BaseSettingsJsonConverter>().FirstOrDefault();
                    if (settingsConverter is not null && settingsConverter.CanConvert(settingsType))
                        settingsConverter.ReadJson(reader, settingsType, settings, serializer);
                }
                catch (JsonSerializationException)
                {
                    return false;
                }
            }

            return true;
        }

        public virtual bool Save(BaseSettings settings, GameDirectory directory, string filename)
        {
            if (GenericServiceProvider.GetService<IFileSystemProvider>() is not { } fileSystemProvider) return false;
            if (fileSystemProvider.GetOrCreateFile(directory, $"{filename}.json") is not { } file) return false;

            var content = SaveJson(settings);

            try
            {
                return fileSystemProvider.WriteData(file, Encoding.UTF8.GetBytes(content));
            }
            catch (Exception)
            {
                return false;
            }
        }
        public virtual BaseSettings Load(BaseSettings settings, GameDirectory directory, string filename)
        {
            if (GenericServiceProvider.GetService<IFileSystemProvider>() is not { } fileSystemProvider) return settings;
            if (fileSystemProvider.GetFile(directory, $"{filename}.json") is not { } file) return settings;
            if (fileSystemProvider.ReadData(file) is not { } data)
            {
                Save(settings, directory, filename);
                return settings;
            }

            try
            {
                if (!TryLoadFromJson(ref settings, Encoding.UTF8.GetString(data)))
                    Save(settings, directory, filename);
            }
            catch (Exception) { }

            return settings;
        }

        protected object? GetSerializationProperty(string path)
        {
            lock (_lock)
            {
                return _existingObjects.TryGetValue(path, out var value) ? value : null;
            }
        }

        protected void AddSerializationProperty(string path, object? value)
        {
            _existingObjects.Add(path, value);
        }

        protected void ClearSerializationProperties()
        {
            _existingObjects.Clear();
        }

        private static bool TryParse(string content, [NotNullWhen(true)] out JObject? jObject)
        {
            try
            {
                jObject = JObject.Parse(content);
                return true;
            }
            catch (JsonReaderException)
            {
                jObject = null;
                return false;
            }
        }
    }
}