feat: Add config list templates command

Lists template YAML files in the trash repo.
pull/201/head
Robert Dailey 1 year ago committed by Robert Dailey
parent 902fbad4bf
commit a3c172cf02

@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
existing CFs that Recyclarr never created in the first place. The default is `true`. existing CFs that Recyclarr never created in the first place. The default is `true`.
- New `quality_profiles` section supported for specifying information about quality profiles. For - New `quality_profiles` section supported for specifying information about quality profiles. For
now, this section doesn't do much, but paves the way for quality profile syncing. now, this section doesn't do much, but paves the way for quality profile syncing.
- New CLI command: `config list` which lists information about current or available configuration
files.
- New `--templates` argument added to `config list` which will list available configuration YAML
templates that can be used in the Trash repo.
### Changed ### Changed

@ -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>

@ -2,12 +2,17 @@
"json_paths": { "json_paths": {
"radarr": { "radarr": {
"custom_formats": ["docs/json/radarr/cf"], "custom_formats": ["docs/json/radarr/cf"],
"qualities": ["docs/json/radarr/quality-size"] "qualities": ["docs/json/radarr/quality-size"],
"naming": ["docs/json/radarr/naming"]
}, },
"sonarr": { "sonarr": {
"release_profiles": ["docs/json/sonarr/rp"], "release_profiles": ["docs/json/sonarr/rp"],
"custom_formats": ["docs/json/sonarr/cf"], "custom_formats": ["docs/json/sonarr/cf"],
"qualities": ["docs/json/sonarr/quality-size"] "qualities": ["docs/json/sonarr/quality-size"],
"naming": ["docs/json/sonarr/naming"]
} }
},
"recyclarr": {
"templates": "docs/recyclarr-configs"
} }
} }

@ -4,7 +4,7 @@ using Autofac;
using Autofac.Features.ResolveAnything; using Autofac.Features.ResolveAnything;
using Recyclarr.Common; using Recyclarr.Common;
using Recyclarr.Common.TestLibrary; using Recyclarr.Common.TestLibrary;
using Recyclarr.TestLibrary; using Recyclarr.TestLibrary.Autofac;
using Recyclarr.TrashLib; using Recyclarr.TrashLib;
using Recyclarr.TrashLib.ApiServices.System; using Recyclarr.TrashLib.ApiServices.System;
using Recyclarr.TrashLib.Repo.VersionControl; using Recyclarr.TrashLib.Repo.VersionControl;

@ -26,6 +26,7 @@ public static class CliSetup
{ {
config.SetDescription("Operations for configuration files"); config.SetDescription("Operations for configuration files");
config.AddCommand<ConfigCreateCommand>("create"); config.AddCommand<ConfigCreateCommand>("create");
config.AddCommand<ConfigListCommand>("list");
}); });
// LEGACY / DEPRECATED SUBCOMMANDS // LEGACY / DEPRECATED SUBCOMMANDS

@ -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;
}
}

@ -23,11 +23,29 @@ public static class CommonMockFileSystemExtensions
fs.AddFileFromEmbeddedResource(path, typeInAssembly, $"{resourceSubPath}.{path.Name}"); fs.AddFileFromEmbeddedResource(path, typeInAssembly, $"{resourceSubPath}.{path.Name}");
} }
public static void AddSameFileFromEmbeddedResource(
this MockFileSystem fs,
string path,
Type typeInAssembly,
string resourceSubPath = "Data")
{
fs.AddFileFromEmbeddedResource(fs.FileInfo.New(path), typeInAssembly, resourceSubPath);
}
public static void AddFileFromEmbeddedResource( public static void AddFileFromEmbeddedResource(
this MockFileSystem fs, this MockFileSystem fs,
IFileInfo path, IFileInfo path,
Type typeInAssembly, Type typeInAssembly,
string embeddedResourcePath) string embeddedResourcePath)
{
fs.AddFileFromEmbeddedResource(path.FullName, typeInAssembly, embeddedResourcePath);
}
public static void AddFileFromEmbeddedResource(
this MockFileSystem fs,
string path,
Type typeInAssembly,
string embeddedResourcePath)
{ {
var resourcePath = $"{typeInAssembly.Namespace}.{embeddedResourcePath}"; var resourcePath = $"{typeInAssembly.Namespace}.{embeddedResourcePath}";
fs.AddFileFromEmbeddedResource(path, typeInAssembly.Assembly, resourcePath); fs.AddFileFromEmbeddedResource(path, typeInAssembly.Assembly, resourcePath);

@ -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");
}
}

@ -6,7 +6,7 @@ using FluentValidation;
using Recyclarr.Cli.TestLibrary; using Recyclarr.Cli.TestLibrary;
using Recyclarr.Common; using Recyclarr.Common;
using Recyclarr.Common.Extensions; using Recyclarr.Common.Extensions;
using Recyclarr.TestLibrary; using Recyclarr.TestLibrary.Autofac;
using Recyclarr.TrashLib.Config.Parsing; using Recyclarr.TrashLib.Config.Parsing;
using Recyclarr.TrashLib.Config.Services.Sonarr; using Recyclarr.TrashLib.Config.Services.Sonarr;
using Recyclarr.TrashLib.Config.Yaml; using Recyclarr.TrashLib.Config.Yaml;

@ -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();
}
}

@ -3,7 +3,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using Recyclarr.Cli.TestLibrary; using Recyclarr.Cli.TestLibrary;
using Recyclarr.TestLibrary; using Recyclarr.TestLibrary.Autofac;
using Recyclarr.TrashLib.Compatibility.Sonarr; using Recyclarr.TrashLib.Compatibility.Sonarr;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Pipelines.ReleaseProfile.Api; using Recyclarr.TrashLib.Pipelines.ReleaseProfile.Api;

@ -1,8 +1,10 @@
using System.Reflection; using System.Reflection;
using Autofac; using Autofac;
using FluentValidation; using FluentValidation;
using Recyclarr.TrashLib.Config.Listers;
using Recyclarr.TrashLib.Config.Parsing; using Recyclarr.TrashLib.Config.Parsing;
using Recyclarr.TrashLib.Config.Secrets; using Recyclarr.TrashLib.Config.Secrets;
using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Config.Settings; using Recyclarr.TrashLib.Config.Settings;
using Recyclarr.TrashLib.Config.Yaml; using Recyclarr.TrashLib.Config.Yaml;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
@ -15,7 +17,7 @@ public class ConfigAutofacModule : Module
{ {
private readonly Assembly[] _assemblies; private readonly Assembly[] _assemblies;
public ConfigAutofacModule(Assembly[] assemblies) public ConfigAutofacModule(params Assembly[] assemblies)
{ {
_assemblies = assemblies; _assemblies = assemblies;
} }
@ -39,5 +41,10 @@ public class ConfigAutofacModule : Module
builder.RegisterType<ConfigurationFinder>().As<IConfigurationFinder>(); builder.RegisterType<ConfigurationFinder>().As<IConfigurationFinder>();
builder.RegisterType<ConfigValidationExecutor>(); builder.RegisterType<ConfigValidationExecutor>();
builder.RegisterType<ConfigParser>(); builder.RegisterType<ConfigParser>();
builder.RegisterType<ConfigTemplateGuideService>().As<IConfigTemplateGuideService>();
// Config Listers
builder.RegisterType<ConfigTemplateLister>().Keyed<IConfigLister>(ConfigListCategory.Templates);
builder.RegisterType<ConfigLocalLister>().Keyed<IConfigLister>(ConfigListCategory.Local);
} }
} }

@ -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();
}

@ -114,7 +114,6 @@ public class ConfigurationLoader : IConfigurationLoader
if (!ParseSingleSection(parser)) if (!ParseSingleSection(parser))
{ {
parser.SkipThisAndNestedEvents(); parser.SkipThisAndNestedEvents();
continue;
} }
} }
} }

@ -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();
}
}

@ -10,5 +10,6 @@ public class ServiceProcessorsAutofacModule : Module
builder.RegisterType<ConfigCreationProcessor>().As<IConfigCreationProcessor>(); builder.RegisterType<ConfigCreationProcessor>().As<IConfigCreationProcessor>();
builder.RegisterType<SyncProcessor>().As<ISyncProcessor>(); builder.RegisterType<SyncProcessor>().As<ISyncProcessor>();
builder.RegisterType<SyncPipelineExecutor>(); builder.RegisterType<SyncPipelineExecutor>();
builder.RegisterType<ConfigListProcessor>();
} }
} }

@ -1,6 +0,0 @@
namespace Recyclarr.TrashLib.Repo;
public interface IRepoMetadataParser
{
RepoMetadata Deserialize();
}

@ -8,7 +8,6 @@ public class RepoAutofacModule : Module
{ {
base.Load(builder); base.Load(builder);
builder.RegisterType<RepoUpdater>().As<IRepoUpdater>(); builder.RegisterType<RepoUpdater>().As<IRepoUpdater>();
builder.RegisterType<RepoMetadataParser>().As<IRepoMetadataParser>();
builder.RegisterType<RepoMetadataBuilder>().As<IRepoMetadataBuilder>().InstancePerLifetimeScope(); builder.RegisterType<RepoMetadataBuilder>().As<IRepoMetadataBuilder>().InstancePerLifetimeScope();
} }
} }

@ -19,7 +19,13 @@ public record JsonPaths
public SonarrMetadata Sonarr { get; init; } = new(); public SonarrMetadata Sonarr { get; init; } = new();
} }
public record RecyclarrMetadata
{
public string Templates { get; init; } = "";
}
public record RepoMetadata public record RepoMetadata
{ {
public JsonPaths JsonPaths { get; init; } = new(); public JsonPaths JsonPaths { get; init; } = new();
public RecyclarrMetadata Recyclarr { get; init; } = new();
} }

@ -8,12 +8,11 @@ public class RepoMetadataBuilder : IRepoMetadataBuilder
private readonly IAppPaths _paths; private readonly IAppPaths _paths;
private readonly Lazy<RepoMetadata> _metadata; private readonly Lazy<RepoMetadata> _metadata;
public RepoMetadataBuilder( public RepoMetadataBuilder(IAppPaths paths)
IRepoMetadataParser parser,
IAppPaths paths)
{ {
_paths = paths; _paths = paths;
_metadata = new Lazy<RepoMetadata>(parser.Deserialize); _metadata = new Lazy<RepoMetadata>(()
=> TrashRepoJsonParser.Deserialize<RepoMetadata>(_paths.RepoDirectory.File("metadata.json")));
} }
public IReadOnlyList<IDirectoryInfo> ToDirectoryInfoList(IEnumerable<string> listOfDirectories) public IReadOnlyList<IDirectoryInfo> ToDirectoryInfoList(IEnumerable<string> listOfDirectories)

@ -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;
}
}

@ -112,6 +112,7 @@
&amp;lt;/Language&amp;gt;&#xD; &amp;lt;/Language&amp;gt;&#xD;
&amp;lt;/profile&amp;gt;&lt;/RIDER_SETTINGS&gt;&lt;CSharpFormatDocComments&gt;True&lt;/CSharpFormatDocComments&gt;&lt;XAMLCollapseEmptyTags&gt;False&lt;/XAMLCollapseEmptyTags&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;/Profile&gt;</s:String> &amp;lt;/profile&amp;gt;&lt;/RIDER_SETTINGS&gt;&lt;CSharpFormatDocComments&gt;True&lt;/CSharpFormatDocComments&gt;&lt;XAMLCollapseEmptyTags&gt;False&lt;/XAMLCollapseEmptyTags&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">Recyclarr Cleanup</s:String> <s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">Recyclarr Cleanup</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Listers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Persister/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Persister/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=radarr/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=radarr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Recyclarr/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Recyclarr/@EntryIndexedValue">True</s:Boolean>

Loading…
Cancel
Save