parent
902fbad4bf
commit
a3c172cf02
@ -0,0 +1,20 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="config list templates" type="DotNetProject" factoryName=".NET Project">
|
||||||
|
<option name="EXE_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0/recyclarr.exe" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="config list templates" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Recyclarr.Cli/bin/Debug/net7.0" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
|
<option name="USE_MONO" value="0" />
|
||||||
|
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||||
|
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Recyclarr.Cli/Recyclarr.Cli.csproj" />
|
||||||
|
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
|
<option name="PROJECT_TFM" value="net7.0" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Build" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -0,0 +1,55 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Recyclarr.Cli.Console.Helpers;
|
||||||
|
using Recyclarr.TrashLib.Config.Listers;
|
||||||
|
using Recyclarr.TrashLib.ExceptionTypes;
|
||||||
|
using Recyclarr.TrashLib.Processors;
|
||||||
|
using Recyclarr.TrashLib.Repo;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Recyclarr.Cli.Console.Commands;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
[Description("List configuration files in various ways.")]
|
||||||
|
public class ConfigListCommand : AsyncCommand<ConfigListCommand.CliSettings>
|
||||||
|
{
|
||||||
|
private readonly ILogger _log;
|
||||||
|
private readonly ConfigListProcessor _processor;
|
||||||
|
private readonly IRepoUpdater _repoUpdater;
|
||||||
|
|
||||||
|
[SuppressMessage("Design", "CA1034:Nested types should not be visible")]
|
||||||
|
public class CliSettings : BaseCommandSettings
|
||||||
|
{
|
||||||
|
[CommandArgument(0, "[ListCategory]")]
|
||||||
|
[EnumDescription<ConfigListCategory>("The type of configuration information to list.")]
|
||||||
|
public ConfigListCategory ListCategory { get; [UsedImplicitly] init; } = ConfigListCategory.Local;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigListCommand(ILogger log, ConfigListProcessor processor, IRepoUpdater repoUpdater)
|
||||||
|
{
|
||||||
|
_log = log;
|
||||||
|
_processor = processor;
|
||||||
|
_repoUpdater = repoUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<int> ExecuteAsync(CommandContext context, CliSettings settings)
|
||||||
|
{
|
||||||
|
await _repoUpdater.UpdateRepo();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_processor.Process(settings.ListCategory);
|
||||||
|
}
|
||||||
|
catch (FileExistsException e)
|
||||||
|
{
|
||||||
|
_log.Error(
|
||||||
|
"The file {ConfigFile} already exists. Please choose another path or " +
|
||||||
|
"delete/move the existing file and run this command again", e.AttemptedPath);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
using Autofac;
|
using Autofac;
|
||||||
|
|
||||||
namespace Recyclarr.TestLibrary;
|
namespace Recyclarr.TestLibrary.Autofac;
|
||||||
|
|
||||||
public static class AutofacTestExtensions
|
public static class AutofacTestExtensions
|
||||||
{
|
{
|
@ -0,0 +1,22 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Autofac.Features.Indexed;
|
||||||
|
|
||||||
|
namespace Recyclarr.TestLibrary.Autofac;
|
||||||
|
|
||||||
|
public class StubAutofacIndex<TKey, TValue> : IIndex<TKey, TValue>
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Dictionary<TKey, TValue> _values = new();
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
_values.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(TKey key, [UnscopedRef] out TValue value)
|
||||||
|
{
|
||||||
|
return _values.TryGetValue(key, out value!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue this[TKey key] => _values[key];
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
using Autofac.Features.Indexed;
|
||||||
|
using Recyclarr.Cli.TestLibrary;
|
||||||
|
using Recyclarr.TrashLib.Config.Listers;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Tests.Config;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
[Parallelizable(ParallelScope.All)]
|
||||||
|
public class ConfigAutofacModuleTest : IntegrationFixture
|
||||||
|
{
|
||||||
|
private static IEnumerable<ConfigListCategory> AllConfigListCategories()
|
||||||
|
{
|
||||||
|
return Enum.GetValues<ConfigListCategory>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(AllConfigListCategories))]
|
||||||
|
public void All_list_category_types_registered(ConfigListCategory category)
|
||||||
|
{
|
||||||
|
var sut = Resolve<IIndex<ConfigListCategory, IConfigLister>>();
|
||||||
|
var result = sut.TryGetValue(category, out _);
|
||||||
|
result.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
using System.IO.Abstractions;
|
||||||
|
using Recyclarr.Cli.TestLibrary;
|
||||||
|
using Recyclarr.TrashLib.Config;
|
||||||
|
using Recyclarr.TrashLib.Config.Listers;
|
||||||
|
using Recyclarr.TrashLib.Config.Services;
|
||||||
|
using Spectre.Console.Testing;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Tests.Config.Listers;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
[Parallelizable(ParallelScope.All)]
|
||||||
|
public class ConfigTemplateListerTest : IntegrationFixture
|
||||||
|
{
|
||||||
|
[Test, AutoMockData]
|
||||||
|
public void Hidden_templates_are_not_rendered(
|
||||||
|
IFileInfo stubFile,
|
||||||
|
[Frozen(Matching.ImplementedInterfaces)] TestConsole console,
|
||||||
|
[Frozen] IConfigTemplateGuideService guideService,
|
||||||
|
ConfigTemplateLister sut)
|
||||||
|
{
|
||||||
|
guideService.TemplateData.Returns(new[]
|
||||||
|
{
|
||||||
|
new TemplatePath(SupportedServices.Radarr, "r1", stubFile, false),
|
||||||
|
new TemplatePath(SupportedServices.Radarr, "r2", stubFile, false),
|
||||||
|
new TemplatePath(SupportedServices.Sonarr, "s1", stubFile, false),
|
||||||
|
new TemplatePath(SupportedServices.Sonarr, "s2", stubFile, true),
|
||||||
|
});
|
||||||
|
|
||||||
|
sut.List();
|
||||||
|
|
||||||
|
console.Output.Should().NotContain("s2");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
using System.IO.Abstractions;
|
||||||
|
using Recyclarr.Cli.TestLibrary;
|
||||||
|
using Recyclarr.Common.Extensions;
|
||||||
|
using Recyclarr.Common.TestLibrary;
|
||||||
|
using Recyclarr.TestLibrary;
|
||||||
|
using Recyclarr.TrashLib.Config;
|
||||||
|
using Recyclarr.TrashLib.Config.Services;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Tests.Config.Services;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
[Parallelizable(ParallelScope.All)]
|
||||||
|
public class ConfigTemplateGuideServiceTest : IntegrationFixture
|
||||||
|
{
|
||||||
|
[Test, AutoMockData]
|
||||||
|
public void Throw_when_templates_dir_does_not_exist(
|
||||||
|
ConfigTemplateGuideService sut)
|
||||||
|
{
|
||||||
|
var act = () => _ = sut.TemplateData;
|
||||||
|
|
||||||
|
act.Should().Throw<InvalidDataException>().WithMessage("Path*templates*");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Normal_behavior()
|
||||||
|
{
|
||||||
|
var templateDir = Paths.RepoDirectory.SubDir("docs/recyclarr-configs");
|
||||||
|
Fs.AddSameFileFromEmbeddedResource(templateDir.File("templates.json"), typeof(ConfigTemplateGuideServiceTest));
|
||||||
|
|
||||||
|
TemplatePath MakeTemplatePath(SupportedServices service, string id, string path)
|
||||||
|
{
|
||||||
|
var fsPath = templateDir.File(path);
|
||||||
|
Fs.AddEmptyFile(fsPath);
|
||||||
|
fsPath.Refresh();
|
||||||
|
return new TemplatePath(service, id, fsPath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedPaths = new[]
|
||||||
|
{
|
||||||
|
MakeTemplatePath(SupportedServices.Radarr, "hd-bluray-web", "radarr/hd-bluray-web.yml"),
|
||||||
|
MakeTemplatePath(SupportedServices.Radarr, "uhd-bluray-web", "radarr/uhd-bluray-web.yml"),
|
||||||
|
MakeTemplatePath(SupportedServices.Sonarr, "web-1080p-v4", "sonarr/web-1080p-v4.yml")
|
||||||
|
};
|
||||||
|
|
||||||
|
var sut = Resolve<ConfigTemplateGuideService>();
|
||||||
|
|
||||||
|
var data = sut.TemplateData;
|
||||||
|
data.Should().BeEquivalentTo(expectedPaths, o => o.Excluding(x => x.TemplateFile));
|
||||||
|
data.Select(x => x.TemplateFile.FullName)
|
||||||
|
.Should().BeEquivalentTo(expectedPaths.Select(x => x.TemplateFile.FullName));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"radarr": [
|
||||||
|
{
|
||||||
|
"template": "radarr/hd-bluray-web.yml",
|
||||||
|
"id": "hd-bluray-web"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"template": "radarr/uhd-bluray-web.yml",
|
||||||
|
"id": "uhd-bluray-web"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sonarr": [
|
||||||
|
{
|
||||||
|
"template": "sonarr/web-1080p-v4.yml",
|
||||||
|
"id": "web-1080p-v4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
using Recyclarr.TestLibrary.Autofac;
|
||||||
|
using Recyclarr.TrashLib.Config.Listers;
|
||||||
|
using Recyclarr.TrashLib.Processors;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Tests.Processors;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
[Parallelizable(ParallelScope.All)]
|
||||||
|
public class ConfigListProcessorTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
[InlineAutoMockData(ConfigListCategory.Templates)]
|
||||||
|
public void List_templates_invokes_correct_lister(
|
||||||
|
ConfigListCategory category,
|
||||||
|
[Frozen(Matching.ImplementedInterfaces)] StubAutofacIndex<ConfigListCategory, IConfigLister> configListers,
|
||||||
|
IConfigLister lister,
|
||||||
|
ConfigListProcessor sut)
|
||||||
|
{
|
||||||
|
configListers.Add(category, lister);
|
||||||
|
|
||||||
|
sut.Process(category);
|
||||||
|
|
||||||
|
lister.Received().List();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Recyclarr.TrashLib.Config.Listers;
|
||||||
|
|
||||||
|
public enum ConfigListCategory
|
||||||
|
{
|
||||||
|
Local,
|
||||||
|
Templates
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Config.Listers;
|
||||||
|
|
||||||
|
public class ConfigLocalLister : IConfigLister
|
||||||
|
{
|
||||||
|
private readonly IAnsiConsole _console;
|
||||||
|
|
||||||
|
public ConfigLocalLister(IAnsiConsole console)
|
||||||
|
{
|
||||||
|
_console = console;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List()
|
||||||
|
{
|
||||||
|
_console.Write("Local listing is not supported yet, but coming soon.");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
using MoreLinq;
|
||||||
|
using Recyclarr.TrashLib.Config.Services;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Config.Listers;
|
||||||
|
|
||||||
|
public class ConfigTemplateLister : IConfigLister
|
||||||
|
{
|
||||||
|
private readonly IAnsiConsole _console;
|
||||||
|
private readonly IConfigTemplateGuideService _guideService;
|
||||||
|
|
||||||
|
public ConfigTemplateLister(IAnsiConsole console, IConfigTemplateGuideService guideService)
|
||||||
|
{
|
||||||
|
_console = console;
|
||||||
|
_guideService = guideService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List()
|
||||||
|
{
|
||||||
|
var data = _guideService.TemplateData;
|
||||||
|
|
||||||
|
var table = new Table();
|
||||||
|
var empty = new Markup("");
|
||||||
|
|
||||||
|
var sonarrRowItems = RenderTemplates(table, data, SupportedServices.Sonarr);
|
||||||
|
var radarrRowItems = RenderTemplates(table, data, SupportedServices.Radarr);
|
||||||
|
var items = radarrRowItems
|
||||||
|
.ZipLongest(sonarrRowItems, (l, r) => (l ?? empty, r ?? empty));
|
||||||
|
|
||||||
|
foreach (var (r, s) in items)
|
||||||
|
{
|
||||||
|
table.AddRow(r, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
_console.Write(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Markup> RenderTemplates(
|
||||||
|
Table table,
|
||||||
|
IEnumerable<TemplatePath> templatePaths,
|
||||||
|
SupportedServices service)
|
||||||
|
{
|
||||||
|
var paths = templatePaths
|
||||||
|
.Where(x => x.Service == service && !x.Hidden)
|
||||||
|
.Select(x => Markup.FromInterpolated($"[blue]{x.Id}[/]"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
table.AddColumn(service.ToString());
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace Recyclarr.TrashLib.Config.Listers;
|
||||||
|
|
||||||
|
public interface IConfigLister
|
||||||
|
{
|
||||||
|
void List();
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO.Abstractions;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Recyclarr.Common.Extensions;
|
||||||
|
using Recyclarr.TrashLib.Repo;
|
||||||
|
using Recyclarr.TrashLib.Startup;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Config.Services;
|
||||||
|
|
||||||
|
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
|
||||||
|
public record TemplateEntry(string Id, string Template, bool Hidden = false);
|
||||||
|
|
||||||
|
public record TemplatesData
|
||||||
|
{
|
||||||
|
public ReadOnlyCollection<TemplateEntry> Radarr { get; [UsedImplicitly] init; } = new(Array.Empty<TemplateEntry>());
|
||||||
|
public ReadOnlyCollection<TemplateEntry> Sonarr { get; [UsedImplicitly] init; } = new(Array.Empty<TemplateEntry>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public record TemplatePath(SupportedServices Service, string Id, IFileInfo TemplateFile, bool Hidden);
|
||||||
|
|
||||||
|
public class ConfigTemplateGuideService : IConfigTemplateGuideService
|
||||||
|
{
|
||||||
|
private readonly IRepoMetadataBuilder _metadataBuilder;
|
||||||
|
private readonly IAppPaths _paths;
|
||||||
|
private readonly Lazy<IReadOnlyCollection<TemplatePath>> _templateData;
|
||||||
|
|
||||||
|
public ConfigTemplateGuideService(
|
||||||
|
IRepoMetadataBuilder metadataBuilder,
|
||||||
|
IAppPaths paths)
|
||||||
|
{
|
||||||
|
_metadataBuilder = metadataBuilder;
|
||||||
|
_paths = paths;
|
||||||
|
_templateData = new Lazy<IReadOnlyCollection<TemplatePath>>(LoadTemplateData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyCollection<TemplatePath> LoadTemplateData()
|
||||||
|
{
|
||||||
|
var metadata = _metadataBuilder.GetMetadata();
|
||||||
|
|
||||||
|
var templatesPath = _paths.RepoDirectory.SubDir(metadata.Recyclarr.Templates);
|
||||||
|
if (!templatesPath.Exists)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException(
|
||||||
|
$"Path to recyclarr templates does not exist: {metadata.Recyclarr.Templates}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var templates = TrashRepoJsonParser.Deserialize<TemplatesData>(templatesPath.File("templates.json"));
|
||||||
|
|
||||||
|
TemplatePath NewTemplatePath(TemplateEntry entry, SupportedServices service)
|
||||||
|
{
|
||||||
|
return new TemplatePath(service, entry.Id, templatesPath.File(entry.Template), entry.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
return templates.Radarr
|
||||||
|
.Select(x => NewTemplatePath(x, SupportedServices.Radarr))
|
||||||
|
.Concat(templates.Sonarr.Select(x => NewTemplatePath(x, SupportedServices.Sonarr)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<TemplatePath> TemplateData => _templateData.Value;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace Recyclarr.TrashLib.Config.Services;
|
||||||
|
|
||||||
|
public interface IConfigTemplateGuideService
|
||||||
|
{
|
||||||
|
IReadOnlyCollection<TemplatePath> TemplateData { get; }
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
using Autofac.Features.Indexed;
|
||||||
|
using Recyclarr.TrashLib.Config.Listers;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Processors;
|
||||||
|
|
||||||
|
public class ConfigListProcessor
|
||||||
|
{
|
||||||
|
private readonly ILogger _log;
|
||||||
|
private readonly IIndex<ConfigListCategory, IConfigLister> _configListers;
|
||||||
|
|
||||||
|
public ConfigListProcessor(ILogger log, IIndex<ConfigListCategory, IConfigLister> configListers)
|
||||||
|
{
|
||||||
|
_log = log;
|
||||||
|
_configListers = configListers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process(ConfigListCategory listCategory)
|
||||||
|
{
|
||||||
|
_log.Debug("Listing configuration for category {Category}", listCategory);
|
||||||
|
if (!_configListers.TryGetValue(listCategory, out var lister))
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(listCategory), listCategory, "Unknown list category");
|
||||||
|
}
|
||||||
|
|
||||||
|
lister.List();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
namespace Recyclarr.TrashLib.Repo;
|
|
||||||
|
|
||||||
public interface IRepoMetadataParser
|
|
||||||
{
|
|
||||||
RepoMetadata Deserialize();
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
using System.IO.Abstractions;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
using Recyclarr.TrashLib.Startup;
|
|
||||||
|
|
||||||
namespace Recyclarr.TrashLib.Repo;
|
|
||||||
|
|
||||||
public class RepoMetadataParser : IRepoMetadataParser
|
|
||||||
{
|
|
||||||
private readonly IAppPaths _paths;
|
|
||||||
|
|
||||||
public RepoMetadataParser(IAppPaths paths)
|
|
||||||
{
|
|
||||||
_paths = paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RepoMetadata Deserialize()
|
|
||||||
{
|
|
||||||
var serializer = JsonSerializer.Create(new JsonSerializerSettings
|
|
||||||
{
|
|
||||||
ContractResolver = new DefaultContractResolver
|
|
||||||
{
|
|
||||||
NamingStrategy = new SnakeCaseNamingStrategy()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var metadataFile = _paths.RepoDirectory.File("metadata.json");
|
|
||||||
using var stream = new JsonTextReader(metadataFile.OpenText());
|
|
||||||
|
|
||||||
var metadata = serializer.Deserialize<RepoMetadata>(stream);
|
|
||||||
if (metadata is null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Unable to deserialize metadata.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,29 @@
|
|||||||
|
using System.IO.Abstractions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Recyclarr.TrashLib.Repo;
|
||||||
|
|
||||||
|
public static class TrashRepoJsonParser
|
||||||
|
{
|
||||||
|
public static T Deserialize<T>(IFileInfo jsonFile)
|
||||||
|
{
|
||||||
|
var serializer = JsonSerializer.Create(new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
ContractResolver = new DefaultContractResolver
|
||||||
|
{
|
||||||
|
NamingStrategy = new SnakeCaseNamingStrategy()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
using var stream = new JsonTextReader(jsonFile.OpenText());
|
||||||
|
|
||||||
|
var obj = serializer.Deserialize<T>(stream);
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"Unable to deserialize {jsonFile}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue