diff --git a/.editorconfig b/.editorconfig
index cb87733a..e9ca7855 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -226,6 +226,12 @@ dotnet_diagnostic.ca1001.severity = none
dotnet_diagnostic.ca1032.severity = none
dotnet_diagnostic.ca1310.severity = none
+# Jump statements should not be redundant
+# https://rules.sonarsource.com/csharp/RSPEC-3626/
+# Justification: Used by Resharper to separate local methods from logic
+dotnet_diagnostic.s3626.severity = none
+dotnet_diagnostic.s1751.severity = none
+
# ReSharper properties
resharper_accessor_owner_body = expression_body
resharper_alignment_tab_fill_style = use_spaces
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 175941ab..e5e7b92b 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -24,6 +24,7 @@
+
diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs
index a115fe72..5f8630cb 100644
--- a/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs
+++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs
@@ -19,11 +19,11 @@ public class ProcessedCustomFormatCache : IPipelineCache
public CustomFormatData? LookupByTrashId(string trashId)
{
- return _customFormats.FirstOrDefault(x => x.TrashId.EqualsIgnoreCase(trashId));
+ return _customFormats.Find(x => x.TrashId.EqualsIgnoreCase(trashId));
}
public CustomFormatData? LookupByServiceId(int id)
{
- return _customFormats.FirstOrDefault(x => x.Id == id);
+ return _customFormats.Find(x => x.Id == id);
}
}
diff --git a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs
index eba04148..27ed1e63 100644
--- a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs
+++ b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs
@@ -37,12 +37,13 @@ public class QualityProfileStatCalculator
return stats;
}
- private void ProfileUpdates(ProfileWithStats stats, QualityProfileDto newDto, QualityProfileDto oldDto)
+ private void ProfileUpdates(ProfileWithStats stats, QualityProfileDto oldDto, QualityProfileDto newDto)
{
Log("Upgrade Allowed", oldDto.UpgradeAllowed, newDto.UpgradeAllowed);
Log("Cutoff", oldDto.Items.FindCutoff(oldDto.Cutoff), newDto.Items.FindCutoff(newDto.Cutoff));
Log("Cutoff Score", oldDto.CutoffFormatScore, newDto.CutoffFormatScore);
Log("Minimum Score", oldDto.MinFormatScore, newDto.MinFormatScore);
+
return;
void Log(string msg, T oldValue, T newValue)
@@ -52,11 +53,10 @@ public class QualityProfileStatCalculator
}
}
- private static void QualityUpdates(ProfileWithStats stats, QualityProfileDto newDto, QualityProfileDto oldDto)
+ private static void QualityUpdates(ProfileWithStats stats, QualityProfileDto oldDto, QualityProfileDto newDto)
{
- var dtoQualities = JToken.FromObject(newDto.Items);
- var updatedQualities = JToken.FromObject(oldDto.Items);
- stats.QualitiesChanged = !JToken.DeepEquals(dtoQualities, updatedQualities);
+ stats.QualitiesChanged = !JToken.DeepEquals(
+ JToken.FromObject(oldDto.Items), JToken.FromObject(newDto.Items));
}
private void ScoreUpdates(
diff --git a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs
index 989db9dd..12361e4b 100644
--- a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs
+++ b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs
@@ -52,7 +52,7 @@ public class ReleaseProfileTransactionPhase
.Where(sonarrProfile =>
{
return sonarrProfile.Name.StartsWithIgnoreCase("[Trash]") &&
- !profiles.Any(profile => sonarrProfile.Name.EndsWithIgnoreCase(profile.Name));
+ !profiles.Exists(profile => sonarrProfile.Name.EndsWithIgnoreCase(profile.Name));
})
.ToList();
}
diff --git a/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs b/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs
index e342384d..52cc970e 100644
--- a/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs
+++ b/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs
@@ -14,8 +14,6 @@ public class TemplateConfigCreator : IConfigCreator
private readonly IConfigTemplateGuideService _templates;
private readonly IAppPaths _paths;
- // private readonly IConfigManipulator _configManipulator;
- // private readonly IAnsiConsole _console;
public TemplateConfigCreator(
ILogger log,
@@ -25,8 +23,6 @@ public class TemplateConfigCreator : IConfigCreator
_log = log;
_templates = templates;
_paths = paths;
- // _configManipulator = configManipulator;
- // _console = console;
}
public bool CanHandle(ICreateConfigSettings settings)
@@ -66,19 +62,6 @@ public class TemplateConfigCreator : IConfigCreator
{
_log.Information("Created configuration file: {Path}", destinationFile);
}
-
- // -- See comment in ConfigManipulator.cs --
- // _configManipulator.LoadAndSave(templateFile, destinationFile, (instanceName, config) =>
- // {
- // _console.MarkupLineInterpolated($"Enter configuration info for instance [green]{instanceName}[/]:");
- // var baseUrl = _console.Prompt(new TextPrompt("Base URL:"));
- // var apiKey = _console.Prompt(new TextPrompt("API Key:"));
- // return config with
- // {
- // BaseUrl = baseUrl,
- // ApiKey = apiKey
- // };
- // });
}
catch (FileExistsException e)
{
diff --git a/src/Recyclarr.Cli/Recyclarr.Cli.csproj b/src/Recyclarr.Cli/Recyclarr.Cli.csproj
index f20fcd33..da8cb40a 100644
--- a/src/Recyclarr.Cli/Recyclarr.Cli.csproj
+++ b/src/Recyclarr.Cli/Recyclarr.Cli.csproj
@@ -13,6 +13,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs b/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs
index 751a2b61..d4a0dbe9 100644
--- a/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs
+++ b/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs
@@ -9,9 +9,8 @@ public class RuntimeValidationService : IRuntimeValidationService
private static Type? GetValidatorInterface(Type type)
{
- return type.GetInterfaces()
- .FirstOrDefault(i
- => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>));
+ return Array.Find(type.GetInterfaces(),
+ i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>));
}
public RuntimeValidationService(IEnumerable validators)
diff --git a/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs b/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs
index d0a24d04..006f12ae 100644
--- a/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs
+++ b/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs
@@ -1,7 +1,9 @@
+using System.Diagnostics.CodeAnalysis;
using Flurl.Http.Configuration;
namespace Recyclarr.Common.Networking;
+[SuppressMessage("SonarCloud", "S4830:Server certificates should be verified during SSL/TLS connections")]
public class UntrustedCertClientFactory : DefaultHttpClientFactory
{
public override HttpMessageHandler CreateMessageHandler()
diff --git a/src/Recyclarr.Common/Recyclarr.Common.csproj b/src/Recyclarr.Common/Recyclarr.Common.csproj
index a1c3cf73..c4c8fd42 100644
--- a/src/Recyclarr.Common/Recyclarr.Common.csproj
+++ b/src/Recyclarr.Common/Recyclarr.Common.csproj
@@ -6,6 +6,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/Recyclarr.Common/ResourceDataReader.cs b/src/Recyclarr.Common/ResourceDataReader.cs
index 21b62bac..e03dc644 100644
--- a/src/Recyclarr.Common/ResourceDataReader.cs
+++ b/src/Recyclarr.Common/ResourceDataReader.cs
@@ -5,7 +5,7 @@ namespace Recyclarr.Common;
public class ResourceDataReader : IResourceDataReader
{
- private readonly Assembly? _assembly;
+ private readonly Assembly _assembly;
private readonly string? _namespace;
private readonly string _subdirectory;
@@ -19,7 +19,8 @@ public class ResourceDataReader : IResourceDataReader
{
_subdirectory = subdirectory;
_namespace = typeWithNamespaceToUse.Namespace;
- _assembly = Assembly.GetAssembly(typeWithNamespaceToUse);
+ _assembly = Assembly.GetAssembly(typeWithNamespaceToUse)
+ ?? throw new ArgumentException("Cannot get assembly from type", nameof(typeWithNamespaceToUse));
}
public string ReadData(string filename)
@@ -49,8 +50,7 @@ public class ResourceDataReader : IResourceDataReader
private string FindResourcePath(string resourcePath)
{
- var foundResource = _assembly?.GetManifestResourceNames()
- .FirstOrDefault(x => x.EndsWith(resourcePath));
+ var foundResource = Array.Find(_assembly.GetManifestResourceNames(), x => x.EndsWith(resourcePath));
if (foundResource is null)
{
throw new ArgumentException($"Embedded resource not found: {resourcePath}");
@@ -61,7 +61,7 @@ public class ResourceDataReader : IResourceDataReader
private string GetResourceData(string resourcePath)
{
- using var stream = _assembly?.GetManifestResourceStream(resourcePath);
+ using var stream = _assembly.GetManifestResourceStream(resourcePath);
if (stream is null)
{
throw new ArgumentException($"Unable to open embedded resource: {resourcePath}");
diff --git a/src/Recyclarr.Gui/Recyclarr.Gui.csproj b/src/Recyclarr.Gui/Recyclarr.Gui.csproj
index 32153d2d..008570ba 100644
--- a/src/Recyclarr.Gui/Recyclarr.Gui.csproj
+++ b/src/Recyclarr.Gui/Recyclarr.Gui.csproj
@@ -14,6 +14,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs b/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs
index e90f4676..09821931 100644
--- a/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs
+++ b/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using Recyclarr.Common;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
@@ -5,6 +6,7 @@ using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config.EnvironmentVariables;
+[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")]
public record EnvironmentVariableTag;
public class EnvironmentVariablesDeserializer : INodeDeserializer
diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs
index f428575a..33dca1b9 100644
--- a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs
+++ b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs
@@ -87,6 +87,7 @@ public record ReleaseProfileConfigYaml
// This is usually empty (or the same as ServiceConfigYaml) on purpose.
// If empty, it is kept around to make it clear that this one is dedicated to Radarr.
+[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")]
public record RadarrConfigYaml : ServiceConfigYaml;
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs
index f1d14823..f98f88bd 100644
--- a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs
+++ b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs
@@ -13,10 +13,6 @@ public class ServiceConfigYamlValidator : AbstractValidator
.WithMessage("{PropertyName} must start with 'http' or 'https'")
.WithName("base_url");
- // RuleFor(x => x.BaseUrl)
- // .When(x => x.BaseUrl is {Length: > 0}, ApplyConditionTo.CurrentValidator)
- // .WithMessage("{PropertyName} must start with 'http' or 'https'");
-
RuleFor(x => x.ApiKey).NotEmpty().WithName("api_key");
RuleFor(x => x.CustomFormats)
diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs b/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs
index 59724258..bf60e077 100644
--- a/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs
+++ b/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs
@@ -20,7 +20,7 @@ public sealed class SyntaxErrorHelper : INodeTypeResolver
// just says "no node type resolver could resolve the type", or something along those lines -- which isn't helpful!
private static void CheckSequenceAssignedToNonSequence(ParsingEvent? nodeEvent, MemberInfo currentType)
{
- if (nodeEvent is SequenceStart && !CollectionKeywords.Any(x => currentType.Name.Contains(x)))
+ if (nodeEvent is SequenceStart && !Array.Exists(CollectionKeywords, x => currentType.Name.Contains(x)))
{
throw new YamlException(nodeEvent.Start, nodeEvent.End,
$"A list/array/sequence is not allowed for {currentType.Name}");
diff --git a/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs b/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs
index e34bbc30..eea5aa1f 100644
--- a/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs
+++ b/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs
@@ -1,9 +1,11 @@
+using System.Diagnostics.CodeAnalysis;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config.Secrets;
+[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")]
public record SecretTag;
public class SecretsDeserializer : INodeDeserializer
diff --git a/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj b/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj
index 0832554c..69094efa 100644
--- a/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj
+++ b/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj
@@ -13,6 +13,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+