diff --git a/.build.Nuke/Build.Compile.cs b/.build.Nuke/Build.Compile.cs index b07b145ffac266baf1465a99f7096b28f3bf439e..e784aba811db1ce7120b12cda85a8dd1d1f6bd8d 100644 --- a/.build.Nuke/Build.Compile.cs +++ b/.build.Nuke/Build.Compile.cs @@ -26,21 +26,21 @@ sealed partial class Build : NukeBuild coverageDirectory.DeleteDirectory(); }); - Target Restore => _ => _ + Target Restore => td => td .DependsOn(Clean) .Executes(() => { - DotNetRestore(s => s + _ = DotNetRestore(s => s .SetProjectFile(solution)); }); - Target Compile => _ => _ + Target Compile => td => td .DependsOn(Restore) .Executes(() => { Log.Debug("Configuration {Configuration}", configurationSet); Log.Debug("configuration {configuration}", configuration); - DotNetBuild(s => s + _ = DotNetBuild(s => s .SetNoLogo(true) .SetProjectFile(solution) .SetConfiguration(configurationSet) diff --git a/.build.Nuke/Build.Container.cs b/.build.Nuke/Build.Container.cs index f80c5a517bbabceb138abb20e185451c92b1955e..888d83183148df3af2195ce8538d68046ee558f9 100644 --- a/.build.Nuke/Build.Container.cs +++ b/.build.Nuke/Build.Container.cs @@ -21,7 +21,7 @@ sealed partial class Build : NukeBuild [Parameter("GitLab Project Full Address")] readonly string containerDefaultRID = "linux-x64"; - public Target CreateContainer => _ => _ + public Target CreateContainer => td => td .DependsOn(Publish) .DependsOn(CheckNewCommits) .OnlyWhenStatic(() => runtimeIdentifier != "win-x64") @@ -34,35 +34,35 @@ sealed partial class Build : NukeBuild tags.AddRange(tagsOriginal); } - // Build the Container image - DockerTasks.DockerBuild(_ => _ - .SetPath(PublishDirectory) - .SetFile($"./Dockerfile") - .SetTag(tags.Select(tag => $"{ContainerRegistryImage}:{tag}").ToArray()) - .SetBuildArg(new[] { $"BASE_IMAGE={BaseImage}", $"COPY_PATH={PublishDirectory}" }) - .SetProcessLogger((outputType, output) => - { - // A bug this log type value - if (outputType != OutputType.Std) - Log.Information(output); - else - Log.Error(output); - }) - ); + // Build the Container image + _ = DockerTasks.DockerBuild(dbs => dbs + .SetPath(PublishDirectory) + .SetFile($"./Dockerfile") + .SetTag(tags.Select(tag => $"{ContainerRegistryImage}:{tag}").ToArray()) + .SetBuildArg([$"BASE_IMAGE={BaseImage}", $"COPY_PATH={PublishDirectory}"]) + .SetProcessLogger((outputType, output) => + { + // A bug this log type value + if (outputType != OutputType.Std) + Log.Information(output); + else + Log.Error(output); + }) + ); - // Log in to the Docker registry - DockerTasks.DockerLogin(_ => _ - .SetServer("registry.gitlab.com") - .SetUsername("gitlab-ci-token") - .SetPassword(GitLab.JobToken) - ); + // Log in to the Docker registry + _ = DockerTasks.DockerLogin(_ => _ + .SetServer("registry.gitlab.com") + .SetUsername("gitlab-ci-token") + .SetPassword(GitLab.JobToken) + ); // Push the container images foreach (var tag in tags) { - DockerTasks.DockerPush(_ => _ - .SetName($"{ContainerRegistryImage}:{tag}") - ); + _ = DockerTasks.DockerPush(_ => _ + .SetName($"{ContainerRegistryImage}:{tag}") + ); // Create a link to the GitLab release var tagLink = GitLabAPIUrl($"?orderBy=NAME&sort=asc&search[]={tag}"); diff --git a/.build.Nuke/Build.GitLab.cs b/.build.Nuke/Build.GitLab.cs index f9b587950ea34b661e50b9e6ea85ff22c3aa0ade..833d9e99b8b383f0a3d1fc3ee2b51021f8880537 100644 --- a/.build.Nuke/Build.GitLab.cs +++ b/.build.Nuke/Build.GitLab.cs @@ -42,7 +42,7 @@ sealed partial class Build : NukeBuild /// One for each runtime identifier. /// /// - public Target CreatePackage => _ => _ + public Target CreatePackage => td => td .DependsOn(Publish) .DependsOn(CheckNewCommits) .Requires(() => gitlabPrivateToken) @@ -80,9 +80,9 @@ sealed partial class Build : NukeBuild using var httpClient = HttpClientGitLabToken(); var response = await httpClient.PutAsync( packageLink, - new StreamContent(fileStream)); + new StreamContent(fileStream)).ConfigureAwait(false); - response.EnsureSuccessStatusCode(); + _ = response.EnsureSuccessStatusCode(); } catch (Exception ex) { @@ -97,7 +97,7 @@ sealed partial class Build : NukeBuild /// Creates a release in the GitLab repository. /// /// - public Target GitLabCreateRelease => _ => _ + public Target GitLabCreateRelease => td => td .DependsOn(GitLabCreateTag) .OnlyWhenStatic(() => HasNewCommits) .Requires(() => gitlabPrivateToken) @@ -113,9 +113,9 @@ sealed partial class Build : NukeBuild tag_name = TagName, name = $"{TagName} {Date}", description = $"Created {Date}" - }); + }).ConfigureAwait(false); - response.EnsureSuccessStatusCode(); + _ = response.EnsureSuccessStatusCode(); } catch (Exception ex) { @@ -128,7 +128,7 @@ sealed partial class Build : NukeBuild /// Creates a tag in the GitLab repository. /// /// - Target GitLabCreateTag => _ => _ + Target GitLabCreateTag => td => td .DependsOn(CheckNewCommits) .After(Compile) .OnlyWhenStatic(() => HasNewCommits) @@ -145,9 +145,9 @@ sealed partial class Build : NukeBuild tag_name = TagName, @ref = GitLab?.CommitRefName ?? GitTasks.GitCurrentCommit(), message = $"Automatic tag creation: {isScheduled} at {Date}" - }); + }).ConfigureAwait(false); - response.EnsureSuccessStatusCode(); + _ = response.EnsureSuccessStatusCode(); } catch (Exception ex) { @@ -197,9 +197,9 @@ sealed partial class Build : NukeBuild { name = itemName, url = itemLink - }); + }).ConfigureAwait(false); - response.EnsureSuccessStatusCode(); + _ = response.EnsureSuccessStatusCode(); } catch (Exception ex) { diff --git a/.build.Nuke/Build.Publish.cs b/.build.Nuke/Build.Publish.cs index fa2684dd008ad53d08d9f257463bb6e5fd290990..042775ff8d15c98248a0c187e180a2d684cd72df 100644 --- a/.build.Nuke/Build.Publish.cs +++ b/.build.Nuke/Build.Publish.cs @@ -11,39 +11,39 @@ namespace SuCoS; /// sealed partial class Build : NukeBuild { - [Parameter("Runtime identifier for the build (e.g., win-x64, linux-x64, osx-x64) (default: linux-x64)")] - readonly string runtimeIdentifier = "linux-x64"; + [Parameter("Runtime identifier for the build (e.g., win-x64, linux-x64, osx-x64) (default: linux-x64)")] + readonly string runtimeIdentifier = "linux-x64"; - [Parameter("publish-directory (default: ./publish/{runtimeIdentifier})")] - readonly AbsolutePath publishDirectory; - AbsolutePath PublishDirectory => publishDirectory ?? RootDirectory / "publish" / runtimeIdentifier; + [Parameter("publish-directory (default: ./publish/{runtimeIdentifier})")] + readonly AbsolutePath publishDirectory; + AbsolutePath PublishDirectory => publishDirectory ?? RootDirectory / "publish" / runtimeIdentifier; - [Parameter("publish-self-contained (default: true)")] - readonly bool publishSelfContained = true; + [Parameter("publish-self-contained (default: true)")] + readonly bool publishSelfContained = true; - [Parameter("publish-single-file (default: true)")] - readonly bool publishSingleFile = true; + [Parameter("publish-single-file (default: true)")] + readonly bool publishSingleFile = true; - [Parameter("publish-trimmed (default: false)")] - readonly bool publishTrimmed = false; + [Parameter("publish-trimmed (default: false)")] + readonly bool publishTrimmed = false; - Target Publish => _ => _ - .DependsOn(Restore) - .Executes(() => - { - DotNetPublish(s => s - .SetNoLogo(true) - .SetProject("source/SuCoS.csproj") - .SetConfiguration(configurationSet) - .SetOutput(PublishDirectory) - .SetRuntime(runtimeIdentifier) - .SetSelfContained(publishSelfContained) - .SetPublishSingleFile(publishSingleFile) - .SetPublishTrimmed(publishTrimmed) - .SetAuthors("Bruno Massa") - .SetVersion(CurrentVersion) - .SetAssemblyVersion(CurrentVersion) - .SetInformationalVersion(CurrentVersion) - ); - }); + Target Publish => td => td + .DependsOn(Restore) + .Executes(() => + { + _ = DotNetPublish(s => s + .SetNoLogo(true) + .SetProject("source/SuCoS.csproj") + .SetConfiguration(configurationSet) + .SetOutput(PublishDirectory) + .SetRuntime(runtimeIdentifier) + .SetSelfContained(publishSelfContained) + .SetPublishSingleFile(publishSingleFile) + .SetPublishTrimmed(publishTrimmed) + .SetAuthors("Bruno Massa") + .SetVersion(CurrentVersion) + .SetAssemblyVersion(CurrentVersion) + .SetInformationalVersion(CurrentVersion) + ); + }); } diff --git a/.build.Nuke/Build.Test.cs b/.build.Nuke/Build.Test.cs index 54f1d524fc5b289a850ca97a7d86d6bd46145cff..76ec9e937a11896cc5d0fd669c84a637cf718a69 100644 --- a/.build.Nuke/Build.Test.cs +++ b/.build.Nuke/Build.Test.cs @@ -26,30 +26,30 @@ sealed partial class Build : NukeBuild static AbsolutePath coverageReportDirectory => coverageDirectory / "report"; static AbsolutePath coverageReportSummaryDirectory => coverageReportDirectory / "Summary.txt"; - Target Test => _ => _ + Target Test => td => td .DependsOn(Compile) .Executes(() => { - coverageResultDirectory.CreateDirectory(); - Coverlet(s => s - .SetTarget("dotnet") - .SetTargetArgs("test --no-build --no-restore") - .SetAssembly(testAssembly) - // .SetThreshold(75) - .SetOutput(coverageResultFile) - .SetFormat(CoverletOutputFormat.cobertura)); + _ = coverageResultDirectory.CreateDirectory(); + _ = Coverlet(s => s + .SetTarget("dotnet") + .SetTargetArgs("test --no-build --no-restore") + .SetAssembly(testAssembly) + // .SetThreshold(75) + .SetOutput(coverageResultFile) + .SetFormat(CoverletOutputFormat.cobertura)); }); - public Target TestReport => _ => _ + public Target TestReport => td => td .DependsOn(Test) .Executes(() => { - coverageReportDirectory.CreateDirectory(); - ReportGenerator(s => s - .SetTargetDirectory(coverageReportDirectory) - .SetReportTypes(new ReportTypes[] { ReportTypes.Html, ReportTypes.TextSummary }) - .SetReports(coverageResultFile) - ); + _ = coverageReportDirectory.CreateDirectory(); + _ = ReportGenerator(s => s + .SetTargetDirectory(coverageReportDirectory) + .SetReportTypes([ReportTypes.Html, ReportTypes.TextSummary]) + .SetReports(coverageResultFile) + ); var summaryText = coverageReportSummaryDirectory.ReadAllLines(); Log.Information(string.Join(Environment.NewLine, summaryText)); }); diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 7def812ce0dc0a3e51e5560b0cd87f877e9eaf09..87122c0e5548fbdef89e2da79b123f09c2bfa9a5 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -1,164 +1,164 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "$ref": "#/definitions/build", - "title": "Build Schema", - "definitions": { - "build": { - "type": "object", - "properties": { - "configuration": { - "type": "string", - "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)" - }, - "containerDefaultRID": { - "type": "string", - "description": "GitLab Project Full Address" - }, - "containerRegistryImage": { - "type": "string", - "description": "GitLab Project CI_REGISTRY_IMAGE" - }, - "Continue": { - "type": "boolean", - "description": "Indicates to continue a previously failed build attempt" - }, - "gitlabPrivateToken": { - "type": "string", - "description": "GitLab private token" - }, - "Help": { - "type": "boolean", - "description": "Shows the help text for this build assembly" - }, - "Host": { - "type": "string", - "description": "Host for execution. Default is 'automatic'", - "enum": [ - "AppVeyor", - "AzurePipelines", - "Bamboo", - "Bitbucket", - "Bitrise", - "GitHubActions", - "GitLab", - "Jenkins", - "Rider", - "SpaceAutomation", - "TeamCity", - "Terminal", - "TravisCI", - "VisualStudio", - "VSCode" - ] - }, - "isScheduled": { - "type": "boolean", - "description": "If the pipeline was triggered by a schedule (or manually)" - }, - "NoLogo": { - "type": "boolean", - "description": "Disables displaying the NUKE logo" - }, - "packageName": { - "type": "string", - "description": "package-name (default: SuCoS)" - }, - "Partition": { - "type": "string", - "description": "Partition to use on CI" - }, - "Plan": { - "type": "boolean", - "description": "Shows the execution plan (HTML)" - }, - "Profile": { - "type": "array", - "description": "Defines the profiles to load", - "items": { - "type": "string" - } - }, - "publishDirectory": { - "type": "string", - "description": "publish-directory (default: ./publish/{runtimeIdentifier})" - }, - "publishSelfContained": { - "type": "boolean", - "description": "publish-self-contained (default: true)" - }, - "publishSingleFile": { - "type": "boolean", - "description": "publish-single-file (default: true)" - }, - "publishTrimmed": { - "type": "boolean", - "description": "publish-trimmed (default: false)" - }, - "Root": { - "type": "string", - "description": "Root directory during build execution" - }, - "runtimeIdentifier": { - "type": "string", - "description": "Runtime identifier for the build (e.g., win-x64, linux-x64, osx-x64) (default: linux-x64)" - }, - "Skip": { - "type": "array", - "description": "List of targets to be skipped. Empty list skips all dependencies", - "items": { - "type": "string", - "enum": [ - "CheckNewCommits", - "Clean", - "Compile", - "CreateContainer", - "CreatePackage", - "GitLabCreateRelease", - "GitLabCreateTag", - "Publish", - "Restore", - "ShowCurrentVersion", - "Test", - "TestReport" - ] - } - }, - "solution": { - "type": "string", - "description": "Path to a solution file that is automatically loaded" - }, - "Target": { - "type": "array", - "description": "List of targets to be invoked. Default is '{default_target}'", - "items": { - "type": "string", - "enum": [ - "CheckNewCommits", - "Clean", - "Compile", - "CreateContainer", - "CreatePackage", - "GitLabCreateRelease", - "GitLabCreateTag", - "Publish", - "Restore", - "ShowCurrentVersion", - "Test", - "TestReport" - ] - } - }, - "Verbosity": { - "type": "string", - "description": "Logging verbosity during build execution. Default is 'Normal'", - "enum": [ - "Minimal", - "Normal", - "Quiet", - "Verbose" - ] - } - } - } - } -} +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/build", + "title": "Build Schema", + "definitions": { + "build": { + "type": "object", + "properties": { + "configuration": { + "type": "string", + "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)" + }, + "containerDefaultRID": { + "type": "string", + "description": "GitLab Project Full Address" + }, + "containerRegistryImage": { + "type": "string", + "description": "GitLab Project CI_REGISTRY_IMAGE" + }, + "Continue": { + "type": "boolean", + "description": "Indicates to continue a previously failed build attempt" + }, + "gitlabPrivateToken": { + "type": "string", + "description": "GitLab private token" + }, + "Help": { + "type": "boolean", + "description": "Shows the help text for this build assembly" + }, + "Host": { + "type": "string", + "description": "Host for execution. Default is 'automatic'", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitbucket", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "isScheduled": { + "type": "boolean", + "description": "If the pipeline was triggered by a schedule (or manually)" + }, + "NoLogo": { + "type": "boolean", + "description": "Disables displaying the NUKE logo" + }, + "packageName": { + "type": "string", + "description": "package-name (default: SuCoS)" + }, + "Partition": { + "type": "string", + "description": "Partition to use on CI" + }, + "Plan": { + "type": "boolean", + "description": "Shows the execution plan (HTML)" + }, + "Profile": { + "type": "array", + "description": "Defines the profiles to load", + "items": { + "type": "string" + } + }, + "publishDirectory": { + "type": "string", + "description": "publish-directory (default: ./publish/{runtimeIdentifier})" + }, + "publishSelfContained": { + "type": "boolean", + "description": "publish-self-contained (default: true)" + }, + "publishSingleFile": { + "type": "boolean", + "description": "publish-single-file (default: true)" + }, + "publishTrimmed": { + "type": "boolean", + "description": "publish-trimmed (default: false)" + }, + "Root": { + "type": "string", + "description": "Root directory during build execution" + }, + "runtimeIdentifier": { + "type": "string", + "description": "Runtime identifier for the build (e.g., win-x64, linux-x64, osx-x64) (default: linux-x64)" + }, + "Skip": { + "type": "array", + "description": "List of targets to be skipped. Empty list skips all dependencies", + "items": { + "type": "string", + "enum": [ + "CheckNewCommits", + "Clean", + "Compile", + "CreateContainer", + "CreatePackage", + "GitLabCreateRelease", + "GitLabCreateTag", + "Publish", + "Restore", + "ShowCurrentVersion", + "Test", + "TestReport" + ] + } + }, + "solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" + }, + "Target": { + "type": "array", + "description": "List of targets to be invoked. Default is '{default_target}'", + "items": { + "type": "string", + "enum": [ + "CheckNewCommits", + "Clean", + "Compile", + "CreateContainer", + "CreatePackage", + "GitLabCreateRelease", + "GitLabCreateTag", + "Publish", + "Restore", + "ShowCurrentVersion", + "Test", + "TestReport" + ] + } + }, + "Verbosity": { + "type": "string", + "description": "Logging verbosity during build execution. Default is 'Normal'", + "enum": [ + "Minimal", + "Normal", + "Quiet", + "Verbose" + ] + } + } + } + } +} diff --git a/source/BaseGeneratorCommand.cs b/source/BaseGeneratorCommand.cs index 3b216ca6114fc75140b1957e26057cf3db345bec..20579c798f613ba86af0e68a0b081f69cd46e9cb 100644 --- a/source/BaseGeneratorCommand.cs +++ b/source/BaseGeneratorCommand.cs @@ -21,22 +21,22 @@ public abstract class BaseGeneratorCommand /// /// The site configuration. /// - protected Site site; + protected Site site { get; set; } /// /// The front matter parser instance. The default is YAML. /// - protected readonly IFrontMatterParser frontMatterParser = new YAMLParser(); + protected IFrontMatterParser frontMatterParser { get; } = new YAMLParser(); /// /// The stopwatch reporter. /// - protected readonly StopwatchReporter stopwatch; + protected StopwatchReporter stopwatch { get; } /// /// The logger (Serilog). /// - protected readonly ILogger logger; + protected ILogger logger { get; } /// /// Initializes a new instance of the class. @@ -45,10 +45,7 @@ public abstract class BaseGeneratorCommand /// The logger instance. Injectable for testing protected BaseGeneratorCommand(IGenerateOptions options, ILogger logger) { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + ArgumentNullException.ThrowIfNull(options); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); stopwatch = new(logger); @@ -68,14 +65,8 @@ public abstract class BaseGeneratorCommand /// protected static ValueTask WhereParamsFilter(FluidValue input, FilterArguments arguments, TemplateContext context) { - if (input is null) - { - throw new ArgumentNullException(nameof(input)); - } - if (arguments is null) - { - throw new ArgumentNullException(nameof(arguments)); - } + ArgumentNullException.ThrowIfNull(input); + ArgumentNullException.ThrowIfNull(arguments); List result = new(); var list = (input as ArrayValue)!.Values; diff --git a/source/BuildCommand.cs b/source/BuildCommand.cs index 46f9bdcea84de99d57e5cd5af541b43ded7686ad..c4bda47b22eb8f4aa8a9f76a3e97a1283954619e 100644 --- a/source/BuildCommand.cs +++ b/source/BuildCommand.cs @@ -54,10 +54,7 @@ public class BuildCommand : BaseGeneratorCommand var outputAbsolutePath = Path.Combine(options.Output, path); var outputDirectory = Path.GetDirectoryName(outputAbsolutePath); - if (!Directory.Exists(outputDirectory)) - { - _ = Directory.CreateDirectory(outputDirectory!); - } + _ = Directory.CreateDirectory(outputDirectory!); // Save the processed output to the final file var result = page.CompleteContent; @@ -73,6 +70,9 @@ public class BuildCommand : BaseGeneratorCommand { var outputAbsolutePath = Path.Combine(options.Output, resource.Permalink!.TrimStart('/')); + var outputDirectory = Path.GetDirectoryName(outputAbsolutePath); + _ = Directory.CreateDirectory(outputDirectory!); + // Copy the file to the output folder File.Copy(resource.SourceFullPath, outputAbsolutePath, overwrite: true); } diff --git a/source/Helpers/FileUtils.cs b/source/Helpers/FileUtils.cs index 5125bb8e8cd36883ab8b9d8d876fb2ddac668245..0afcb810407ef154fdbd3545804d7aa8ab9888fa 100644 --- a/source/Helpers/FileUtils.cs +++ b/source/Helpers/FileUtils.cs @@ -17,14 +17,8 @@ public static class FileUtils /// The content of the template file. public static string GetTemplate(string themePath, Page page, SiteCacheManager cacheManager, bool isBaseTemplate = false) { - if (page is null) - { - throw new ArgumentNullException(nameof(page)); - } - if (cacheManager is null) - { - throw new ArgumentNullException(nameof(cacheManager)); - } + ArgumentNullException.ThrowIfNull(page); + ArgumentNullException.ThrowIfNull(cacheManager); var index = (page.Section, page.Kind, page.Type); @@ -53,10 +47,7 @@ public static class FileUtils /// The content of the template file, or an empty string if not found. private static string GetTemplate(List templatePaths) { - if (templatePaths is null) - { - throw new ArgumentNullException(nameof(templatePaths)); - } + ArgumentNullException.ThrowIfNull(templatePaths); // Iterate through the template paths and return the content of the first existing file foreach (var templatePath in templatePaths.Where(File.Exists)) @@ -76,10 +67,7 @@ public static class FileUtils /// The list of template paths in the lookup order. private static List GetTemplateLookupOrder(string themePath, Page page, bool isBaseTemplate) { - if (page is null) - { - throw new ArgumentNullException(nameof(page)); - } + ArgumentNullException.ThrowIfNull(page); // Generate the lookup order for template files based on the theme path, page section, type, and kind var sections = page.Section is not null ? new[] { page.Section, string.Empty } : new[] { string.Empty }; diff --git a/source/Helpers/SiteCacheManager.cs b/source/Helpers/SiteCacheManager.cs index 5391bbffe712f3994682cf2a9ea7a19bda74f421..7365db8d0b1bceea36a125a2d65a6173129d8163 100644 --- a/source/Helpers/SiteCacheManager.cs +++ b/source/Helpers/SiteCacheManager.cs @@ -11,17 +11,17 @@ public class SiteCacheManager /// /// Cache for content templates. /// - public readonly Dictionary<(string?, Kind?, string?), string> contentTemplateCache = new(); + public Dictionary<(string?, Kind?, string?), string> contentTemplateCache { get; } = new(); /// /// Cache for base templates. /// - public readonly Dictionary<(string?, Kind?, string?), string> baseTemplateCache = new(); + public Dictionary<(string?, Kind?, string?), string> baseTemplateCache { get; } = new(); /// /// Cache for tag page. /// - public readonly ConcurrentDictionary> automaticContentCache = new(); + public ConcurrentDictionary> automaticContentCache { get; } = new(); /// /// Resets the template cache to force a reload of all templates. diff --git a/source/Helpers/SiteHelper.cs b/source/Helpers/SiteHelper.cs index ebc73468a37290c1071b02f190d1de74e09a98e8..64d2d104b50c76f94005d9cd8d230b54900ddfa8 100644 --- a/source/Helpers/SiteHelper.cs +++ b/source/Helpers/SiteHelper.cs @@ -27,10 +27,7 @@ public static class SiteHelper /// public static Site Init(string configFile, IGenerateOptions options, IFrontMatterParser frontMatterParser, FilterDelegate whereParamsFilter, ILogger logger, StopwatchReporter stopwatch) { - if (stopwatch is null) - { - throw new ArgumentNullException(nameof(stopwatch)); - } + ArgumentNullException.ThrowIfNull(stopwatch); SiteSettings siteSettings; try @@ -54,7 +51,7 @@ public static class SiteHelper site.ParseAndScanSourceFiles(site.SourceContentPath); - stopwatch.Stop("Parse", site.filesParsedToReport); + stopwatch.Stop("Parse", site.FilesParsedToReport); site.TemplateOptions.FileProvider = new PhysicalFileProvider(Path.GetFullPath(site.SourceThemePath)); @@ -97,14 +94,8 @@ public static class SiteHelper /// The site settings. private static SiteSettings ParseSettings(string configFile, IGenerateOptions options, IFrontMatterParser frontMatterParser) { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } - if (frontMatterParser is null) - { - throw new ArgumentNullException(nameof(frontMatterParser)); - } + ArgumentNullException.ThrowIfNull(options); + ArgumentNullException.ThrowIfNull(frontMatterParser); try { diff --git a/source/Helpers/SourceFileWatcher.cs b/source/Helpers/SourceFileWatcher.cs index b857e4c5fee05e65994fe1344c51d6ff43b1f8dc..4da2e2da1ffe347746264be2243b1e2668e40344 100644 --- a/source/Helpers/SourceFileWatcher.cs +++ b/source/Helpers/SourceFileWatcher.cs @@ -3,7 +3,7 @@ namespace SuCoS.Helpers; /// /// The FileSystemWatcher object that monitors the source directory for file changes. /// -public class SourceFileWatcher : IFileWatcher +public sealed class SourceFileWatcher : IFileWatcher, IDisposable { /// /// The FileSystemWatcher object that monitors the source directory for file changes. @@ -16,10 +16,7 @@ public class SourceFileWatcher : IFileWatcher /// public void Start(string SourceAbsolutePath, Action OnSourceFileChanged) { - if (OnSourceFileChanged is null) - { - throw new ArgumentNullException(nameof(OnSourceFileChanged)); - } + ArgumentNullException.ThrowIfNull(OnSourceFileChanged); fileWatcher = new FileSystemWatcher { @@ -41,4 +38,19 @@ public class SourceFileWatcher : IFileWatcher { fileWatcher?.Dispose(); } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) + { + if (disposing) + { + fileWatcher?.Dispose(); + } + } } diff --git a/source/Models/FrontMatter.cs b/source/Models/FrontMatter.cs index 5bb5d76a8d671ac0a711c806dcc73247445658f6..11c5d94a878b6bf7072b886b736abb5c6bceb522 100644 --- a/source/Models/FrontMatter.cs +++ b/source/Models/FrontMatter.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using YamlDotNet.Serialization; namespace SuCoS.Models; @@ -23,7 +24,7 @@ public class FrontMatter : IFrontMatter public bool? Draft { get; init; } /// - public List? Aliases { get; init; } + public Collection? Aliases { get; init; } /// public string? Section { get; set; } = string.Empty; @@ -41,13 +42,13 @@ public class FrontMatter : IFrontMatter public DateTime? ExpiryDate { get; init; } /// - public int Weight { get; init; } = 0; + public int Weight { get; init; } /// - public List? Tags { get; init; } + public Collection? Tags { get; init; } /// - public List? ResourceDefinitions { get; set; } + public Collection? ResourceDefinitions { get; set; } /// [YamlIgnore] diff --git a/source/Models/IFrontMatter.cs b/source/Models/IFrontMatter.cs index c87eadb2b43f961c8f758021a56da522dcf1ea20..50d736125c7fe3e68137d3a9c9daff4d0ed19af2 100644 --- a/source/Models/IFrontMatter.cs +++ b/source/Models/IFrontMatter.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using SuCoS.Models.CommandLineOptions; namespace SuCoS.Models; @@ -82,7 +83,7 @@ public interface IFrontMatter : IParams, IFile /// List URL, it will be parsed as liquid templates, so you can use page variables. /// /// - List? Aliases { get; } + Collection? Aliases { get; } /// /// Page weight. Used for sorting by default. @@ -92,12 +93,12 @@ public interface IFrontMatter : IParams, IFile /// /// A list of tags, if any. /// - List? Tags { get; } + Collection? Tags { get; } /// /// List of resource definitions. /// - List? ResourceDefinitions { get; } + Collection? ResourceDefinitions { get; } /// /// Raw content from the Markdown file, bellow the front matter. diff --git a/source/Models/IPage.cs b/source/Models/IPage.cs index d979b7f5b581ca601908279ac7cfb76558bcd61f..83b7e0e22d3db379be2a5d930c06bcf1ec73e45f 100644 --- a/source/Models/IPage.cs +++ b/source/Models/IPage.cs @@ -1,6 +1,7 @@ using Markdig; using SuCoS.Helpers; using System.Collections.Concurrent; +using System.Collections.ObjectModel; namespace SuCoS.Models; @@ -27,7 +28,7 @@ public interface IPage : IFrontMatter, IOutput /// /// Secondary URL patterns to be used to create the url. /// - public List? AliasesProcessed { get; set; } + public Collection? AliasesProcessed { get; } /// /// Other content that mention this content. @@ -49,7 +50,7 @@ public interface IPage : IFrontMatter, IOutput /// /// Page resources. All files that accompany a page. /// - public List? Resources { get; set; } + public Collection? Resources { get; } /// /// Plain markdown content, without HTML. @@ -81,7 +82,7 @@ public interface IPage : IFrontMatter, IOutput /// public int WordCount => Plain.Split(nonWords, StringSplitOptions.RemoveEmptyEntries).Length; - private static readonly char[] nonWords = { ' ', ',', ';', '.', '!', '"', '(', ')', '?', '\n', '\r' }; + private static readonly char[] nonWords = [' ', ',', ';', '.', '!', '"', '(', ')', '?', '\n', '\r']; /// /// The markdown content converted to HTML diff --git a/source/Models/Page.cs b/source/Models/Page.cs index 5f88cb54a162b42e87e4e027ef60c92b82a43714..d64dd5c3bc6c12a89120f79cbef2c5b993a0fe23 100644 --- a/source/Models/Page.cs +++ b/source/Models/Page.cs @@ -3,6 +3,7 @@ using Markdig; using Microsoft.Extensions.FileSystemGlobbing; using SuCoS.Helpers; using System.Collections.Concurrent; +using System.Collections.ObjectModel; namespace SuCoS.Models; @@ -28,7 +29,7 @@ public class Page : IPage public bool? Draft => frontMatter.Draft; /// - public List? Aliases => frontMatter.Aliases; + public Collection? Aliases => frontMatter.Aliases; /// public string? Section => frontMatter.Section; @@ -49,10 +50,10 @@ public class Page : IPage public int Weight => frontMatter.Weight; /// - public List? Tags => frontMatter.Tags; + public Collection? Tags => frontMatter.Tags; /// - public List? ResourceDefinitions => frontMatter.ResourceDefinitions; + public Collection? ResourceDefinitions => frontMatter.ResourceDefinitions; /// public string RawContent => frontMatter.RawContent; @@ -103,7 +104,7 @@ public class Page : IPage /// /// Secondary URL patterns to be used to create the url. /// - public List? AliasesProcessed { get; set; } + public Collection? AliasesProcessed { get; set; } /// public string? Permalink { get; set; } @@ -121,7 +122,7 @@ public class Page : IPage public BundleType BundleType { get; set; } = BundleType.none; /// - public List? Resources { get; set; } + public Collection? Resources { get; set; } /// /// Plain markdown content, without HTML. @@ -153,7 +154,7 @@ public class Page : IPage /// public int WordCount => Plain.Split(nonWords, StringSplitOptions.RemoveEmptyEntries).Length; - private static readonly char[] nonWords = { ' ', ',', ';', '.', '!', '"', '(', ')', '?', '\n', '\r' }; + private static readonly char[] nonWords = [' ', ',', ';', '.', '!', '"', '(', ')', '?', '\n', '\r']; /// /// The markdown content converted to HTML @@ -191,7 +192,7 @@ public class Page : IPage return pagesCached; } - pagesCached ??= new(); + pagesCached = new(); foreach (var permalink in PagesReferences) { var page = Site.OutputReferences[permalink] as IPage; @@ -365,14 +366,14 @@ endif { foreach (var tagName in Tags) { - Site.CreateSystemPage(Path.Combine("tags", tagName), tagName, "tags", this); + _ = Site.CreateSystemPage(Path.Combine("tags", tagName), tagName, "tags", this); } } ScanForResources(); } - private int counterInternal = 0; + private int counterInternal; private bool counterInternalLock; private int counter { @@ -418,7 +419,7 @@ endif foreach (var resourceDefinition in ResourceDefinitions) { resourceDefinition.GlobMatcher ??= new(); - resourceDefinition.GlobMatcher.AddInclude(resourceDefinition.Src); + _ = resourceDefinition.GlobMatcher.AddInclude(resourceDefinition.Src); var file = new InMemoryDirectoryInfo("./", new[] { filenameOriginal }); if (resourceDefinition.GlobMatcher.Execute(file).HasMatches) { diff --git a/source/Models/Site.cs b/source/Models/Site.cs index 3df334f2f735fed258eab2fec8a40bfc5b5b2f8f..4b56ce34943440194a34f0a4431d05289e72e550 100644 --- a/source/Models/Site.cs +++ b/source/Models/Site.cs @@ -141,7 +141,9 @@ public class Site : ISite /// /// Number of files parsed, used in the report. /// - public int filesParsedToReport; + public int FilesParsedToReport => filesParsedToReport; + + private int filesParsedToReport; private const string indexLeafFileConst = "index.md"; @@ -218,7 +220,7 @@ public class Site : ISite _ = Parallel.ForEach(markdownFiles, filePath => { - ParseSourceFile(filePath, parent); + _ = ParseSourceFile(filePath, parent); }); var subdirectories = Directory.GetDirectories(directory); @@ -310,7 +312,7 @@ public class Site : ISite // Remove the selected file from markdownFiles markdownFiles = bundleType == BundleType.leaf - ? new string[] { } + ? Array.Empty() : markdownFiles.Where(file => file != selectedFile).ToArray(); page = ParseSourceFile(selectedFile!, parent, bundleType); @@ -318,11 +320,11 @@ public class Site : ISite if (level == 0) { - OutputReferences.TryRemove(page!.Permalink!, out _); + _ = OutputReferences.TryRemove(page!.Permalink!, out _); page.Permalink = "/"; page.Kind = Kind.index; - OutputReferences.GetOrAdd(page.Permalink, page); + _ = OutputReferences.GetOrAdd(page.Permalink, page); Home = page; } else @@ -341,7 +343,7 @@ public class Site : ISite } } - private IPage? ParseSourceFile(in string filePath, in IPage? parent, BundleType bundleType = BundleType.none) + private Page? ParseSourceFile(in string filePath, in IPage? parent, BundleType bundleType = BundleType.none) { Page? page = null; try @@ -377,10 +379,7 @@ public class Site : ISite /// public void PostProcessPage(in IPage page, IPage? parent = null, bool overwrite = false) { - if (page is null) - { - throw new ArgumentNullException(nameof(page)); - } + ArgumentNullException.ThrowIfNull(page); page.Parent = parent; page.Permalink = page.CreatePermalink(); @@ -391,7 +390,7 @@ public class Site : ISite page.PostProcess(); // Replace the old page with the newly created one - if (oldOutput is IPage oldpage && oldpage?.PagesReferences is not null) + if (oldOutput is IPage oldpage && oldpage.PagesReferences is not null) { foreach (var pageOld in oldpage.PagesReferences) { @@ -402,7 +401,7 @@ public class Site : ISite // Register the page for all urls foreach (var pageOutput in page.AllOutputURLs) { - OutputReferences.TryAdd(pageOutput.Key, pageOutput.Value); + _ = OutputReferences.TryAdd(pageOutput.Key, pageOutput.Value); } } } @@ -420,10 +419,8 @@ public class Site : ISite /// public bool IsValidPage(in IFrontMatter frontMatter, IGenerateOptions? options) { - if (frontMatter is null) - { - throw new ArgumentNullException(nameof(frontMatter)); - } + ArgumentNullException.ThrowIfNull(frontMatter); + return IsValidDate(frontMatter, options) && (frontMatter.Draft is null || frontMatter.Draft == false || (options?.Draft ?? false)); } @@ -431,10 +428,8 @@ public class Site : ISite /// public bool IsValidDate(in IFrontMatter frontMatter, IGenerateOptions? options) { - if (frontMatter is null) - { - throw new ArgumentNullException(nameof(frontMatter)); - } + ArgumentNullException.ThrowIfNull(frontMatter); + return (!IsDateExpired(frontMatter) || (options?.Expired ?? false)) && (IsDatePublishable(frontMatter) || (options?.Future ?? false)); } @@ -444,10 +439,8 @@ public class Site : ISite /// public bool IsDateExpired(in IFrontMatter frontMatter) { - if (frontMatter is null) - { - throw new ArgumentNullException(nameof(frontMatter)); - } + ArgumentNullException.ThrowIfNull(frontMatter); + return frontMatter.ExpiryDate is not null && frontMatter.ExpiryDate <= clock.Now; } @@ -456,10 +449,8 @@ public class Site : ISite /// public bool IsDatePublishable(in IFrontMatter frontMatter) { - if (frontMatter is null) - { - throw new ArgumentNullException(nameof(frontMatter)); - } + ArgumentNullException.ThrowIfNull(frontMatter); + return frontMatter.GetPublishDate is null || frontMatter.GetPublishDate <= clock.Now; } } \ No newline at end of file diff --git a/source/Parser/YAMLParser.cs b/source/Parser/YAMLParser.cs index 36ca94dd8024cb0f0f5af565cc10a8dfdfc3dcaa..f9b85d2f5930dfcd428a7359cd2245e6c71e2457 100644 --- a/source/Parser/YAMLParser.cs +++ b/source/Parser/YAMLParser.cs @@ -27,10 +27,7 @@ public class YAMLParser : IFrontMatterParser /// public IFrontMatter ParseFrontmatterAndMarkdownFromFile(in string fileFullPath, in string? sourceContentPath = null) { - if (fileFullPath is null) - { - throw new ArgumentNullException(nameof(fileFullPath)); - } + ArgumentNullException.ThrowIfNull(fileFullPath); string? fileContent; string? fileRelativePath; @@ -50,10 +47,7 @@ public class YAMLParser : IFrontMatterParser /// public IFrontMatter ParseFrontmatterAndMarkdown(in string fileFullPath, in string fileRelativePath, in string fileContent) { - if (fileRelativePath is null) - { - throw new ArgumentNullException(nameof(fileRelativePath)); - } + ArgumentNullException.ThrowIfNull(fileRelativePath); using var content = new StringReader(fileContent); var frontMatterBuilder = new StringBuilder(); @@ -62,7 +56,7 @@ public class YAMLParser : IFrontMatterParser while ((line = content.ReadLine()) != null && line != "---") { } while ((line = content.ReadLine()) != null && line != "---") { - frontMatterBuilder.AppendLine(line); + _ = frontMatterBuilder.AppendLine(line); } // Join the read lines to form the front matter @@ -75,7 +69,7 @@ public class YAMLParser : IFrontMatterParser return page; } - private IFrontMatter ParseYAML(in string fileFullPath, in string fileRelativePath, string yaml, in string rawContent) + private FrontMatter ParseYAML(in string fileFullPath, in string fileRelativePath, string yaml, in string rawContent) { var frontMatter = yamlDeserializerRigid.Deserialize(new StringReader(yaml)) ?? throw new FormatException("Error parsing front matter"); var section = SiteHelper.GetSection(fileRelativePath); @@ -112,14 +106,8 @@ public class YAMLParser : IFrontMatterParser /// yamlObject already parsed if available public void ParseParams(IParams settings, Type type, string yaml, object? yamlObject = null) { - if (settings is null) - { - throw new ArgumentNullException(nameof(settings)); - } - if (type is null) - { - throw new ArgumentNullException(nameof(type)); - } + ArgumentNullException.ThrowIfNull(settings); + ArgumentNullException.ThrowIfNull(type); yamlObject ??= yamlDeserializer.Deserialize(new StringReader(yaml)); if (yamlObject is not Dictionary yamlDictionary) diff --git a/source/Program.cs b/source/Program.cs index 917a393b1eef09a90c9661cd63b7ba2e81f1d4f9..231a56f2e12eb59ed4f17807a2d37307173346d3 100644 --- a/source/Program.cs +++ b/source/Program.cs @@ -26,6 +26,12 @@ public class Program "; private ILogger logger; + private static readonly string[] aliases = ["--source", "-s"]; + private static readonly string[] aliasesArray = ["--draft", "-d"]; + private static readonly string[] aliasesArray0 = ["--future", "-f"]; + private static readonly string[] aliasesArray1 = ["--expired", "-e"]; + private static readonly string[] aliasesArray2 = ["--verbose", "-v"]; + private static readonly string[] aliasesArray3 = ["--output", "-o"]; /// /// Entry point of the program @@ -59,14 +65,14 @@ public class Program OutputWelcome(); // Shared options between the commands - var sourceOption = new Option(new[] { "--source", "-s" }, () => ".", "Source directory path"); - var draftOption = new Option(new[] { "--draft", "-d" }, "Include draft content"); - var futureOption = new Option(new[] { "--future", "-f" }, "Include content with dates in the future"); - var expiredOption = new Option(new[] { "--expired", "-e" }, "Include content with ExpiredDate dates from the past"); - var verboseOption = new Option(new[] { "--verbose", "-v" }, "Verbose output"); + var sourceOption = new Option(aliases, () => ".", "Source directory path"); + var draftOption = new Option(aliasesArray, "Include draft content"); + var futureOption = new Option(aliasesArray0, "Include content with dates in the future"); + var expiredOption = new Option(aliasesArray1, "Include content with ExpiredDate dates from the past"); + var verboseOption = new Option(aliasesArray2, "Verbose output"); // BuildCommand setup - var buildOutputOption = new Option(new[] { "--output", "-o" }, "Output directory path"); + var buildOutputOption = new Option(aliasesArray3, "Output directory path"); Command buildCommandHandler = new("build", "Builds the site") { @@ -116,7 +122,7 @@ public class Program var serveCommand = new ServeCommand(serverOptions, logger, new SourceFileWatcher()); serveCommand.StartServer(); - await Task.Delay(-1); // Wait forever. + await Task.Delay(-1).ConfigureAwait(false); // Wait forever. }, sourceOption, draftOption, futureOption, expiredOption, verboseOption); diff --git a/source/ServeCommand.cs b/source/ServeCommand.cs index a0044ae4712c26780645af10796815066a3c7b93..5a0c0d8a7d5b6ea88801b7d8a5c5953a08127757 100644 --- a/source/ServeCommand.cs +++ b/source/ServeCommand.cs @@ -9,7 +9,7 @@ namespace SuCoS; /// /// Serve Command will live serve the site and watch any changes. /// -public class ServeCommand : BaseGeneratorCommand, IDisposable +public sealed class ServeCommand : BaseGeneratorCommand, IDisposable { private const string baseURLDefault = "http://localhost"; private const int portDefault = 1122; @@ -86,13 +86,13 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable serverStartTime = DateTime.UtcNow; - handlers = new IServerHandlers[]{ + handlers = [ new PingRequests(), new StaticFileRequest(site.SourceStaticPath, false), new StaticFileRequest(site.SourceThemeStaticPath, true), new RegisteredPageRequest(site), new RegisteredPageResourceRequest(site) - }; + ]; listener = new HttpListener(); listener.Prefixes.Add($"{baseURL}:{port}/"); listener.Start(); @@ -105,8 +105,8 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable { try { - var context = await listener.GetContextAsync(); - await HandleRequest(context); + var context = await listener.GetContextAsync().ConfigureAwait(false); + await HandleRequest(context).ConfigureAwait(false); } catch (HttpListenerException ex) { @@ -143,7 +143,7 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable /// private async Task RestartServer() { - await lastRestartTask.ContinueWith(async _ => + _ = await lastRestartTask.ContinueWith(async _ => { logger.Information($"Restarting server..."); @@ -155,7 +155,7 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable if (loop is not null) { // Wait for the loop to finish processing any ongoing requests. - await loop; + await loop.ConfigureAwait(false); loop.Dispose(); } } @@ -164,7 +164,7 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable site = SiteHelper.Init(configFile, options, frontMatterParser, WhereParamsFilter, logger, stopwatch); StartServer(baseURLDefault, portDefault); - }); + }).ConfigureAwait(false); lastRestartTask = lastRestartTask.ContinueWith(t => t.Exception != null ? throw t.Exception @@ -194,7 +194,7 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable try { - resultType = await item.Handle(response, requestPath, serverStartTime); + resultType = await item.Handle(response, requestPath, serverStartTime).ConfigureAwait(false); } catch (Exception ex) { @@ -212,7 +212,7 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable if (resultType is null) { resultType = "404"; - await HandleNotFoundRequest(context); + await HandleNotFoundRequest(context).ConfigureAwait(false); } logger.Debug("Request {type}\tfor {RequestPath}", resultType, requestPath); } @@ -221,7 +221,7 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable { context.Response.StatusCode = 404; using var writer = new StreamWriter(context.Response.OutputStream); - await writer.WriteAsync("404 - File Not Found"); + await writer.WriteAsync("404 - File Not Found").ConfigureAwait(false); } /// @@ -241,6 +241,6 @@ public class ServeCommand : BaseGeneratorCommand, IDisposable private async void DebounceCallback(object? state) { - await RestartServer(); + await RestartServer().ConfigureAwait(false); } } diff --git a/source/ServerHandlers/PingRequests.cs b/source/ServerHandlers/PingRequests.cs index ba5e4394c13d29ede104ef0860bad6ea625b203f..3d2f57130c40b55c45a9f79add04c413463b4ec0 100644 --- a/source/ServerHandlers/PingRequests.cs +++ b/source/ServerHandlers/PingRequests.cs @@ -14,15 +14,13 @@ public class PingRequests : IServerHandlers /// public async Task Handle(IHttpListenerResponse response, string requestPath, DateTime serverStartTime) { - if (response is null) - { - throw new ArgumentNullException(nameof(response)); - } + ArgumentNullException.ThrowIfNull(response); + var content = serverStartTime.ToString("o"); using var writer = new StreamWriter(response.OutputStream, leaveOpen: true); - await writer.WriteAsync(content); - await writer.FlushAsync(); + await writer.WriteAsync(content).ConfigureAwait(false); + await writer.FlushAsync().ConfigureAwait(false); return "ping"; } diff --git a/source/ServerHandlers/RegisteredPageRequest.cs b/source/ServerHandlers/RegisteredPageRequest.cs index a80f5c6bf59d799e90f136f5075bd8d0ed15eeb6..7c285aa3dc45dc03400ad1db3eec4454947f45c9 100644 --- a/source/ServerHandlers/RegisteredPageRequest.cs +++ b/source/ServerHandlers/RegisteredPageRequest.cs @@ -22,26 +22,22 @@ public class RegisteredPageRequest : IServerHandlers /// public bool Check(string requestPath) { - if (requestPath is null) - { - throw new ArgumentNullException(nameof(requestPath)); - } + ArgumentNullException.ThrowIfNull(requestPath); + return site.OutputReferences.TryGetValue(requestPath, out var item) && item is IPage _; } /// public async Task Handle(IHttpListenerResponse response, string requestPath, DateTime serverStartTime) { - if (response is null) - { - throw new ArgumentNullException(nameof(response)); - } + ArgumentNullException.ThrowIfNull(response); + if (site.OutputReferences.TryGetValue(requestPath, out var output) && output is IPage page) { var content = page.CompleteContent; content = InjectReloadScript(content); using var writer = new StreamWriter(response.OutputStream, leaveOpen: true); - await writer.WriteAsync(content); + await writer.WriteAsync(content).ConfigureAwait(false); return "dict"; } else diff --git a/source/ServerHandlers/RegisteredPageResourceRequest.cs b/source/ServerHandlers/RegisteredPageResourceRequest.cs index 0633c5f3927781e1ec721639b21fe309f3cece43..4d117e326747a75aa1f0c4973624d475524580e2 100644 --- a/source/ServerHandlers/RegisteredPageResourceRequest.cs +++ b/source/ServerHandlers/RegisteredPageResourceRequest.cs @@ -22,26 +22,21 @@ public class RegisteredPageResourceRequest : IServerHandlers /// public bool Check(string requestPath) { - if (requestPath is null) - { - throw new ArgumentNullException(nameof(requestPath)); - } + ArgumentNullException.ThrowIfNull(requestPath); + return site.OutputReferences.TryGetValue(requestPath, out var item) && item is IResource _; } /// public async Task Handle(IHttpListenerResponse response, string requestPath, DateTime serverStartTime) { - if (response is null) - { - throw new ArgumentNullException(nameof(response)); - } + ArgumentNullException.ThrowIfNull(response); if (site.OutputReferences.TryGetValue(requestPath, out var output) && output is IResource resource) { response.ContentType = resource.MimeType; await using var fileStream = new FileStream(resource.SourceFullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - await fileStream.CopyToAsync(response.OutputStream); + await fileStream.CopyToAsync(response.OutputStream).ConfigureAwait(false); return "resource"; } else @@ -56,7 +51,7 @@ public class RegisteredPageResourceRequest : IServerHandlers /// /// The content to inject the reload script into. /// The content with the reload script injected. - private string InjectReloadScript(string content) + private static string InjectReloadScript(string content) { // Read the content of the JavaScript file string scriptContent; diff --git a/source/ServerHandlers/StaticFileRequest.cs b/source/ServerHandlers/StaticFileRequest.cs index d3f3640d733b269b595d9e23a6ec86b60e5b5d71..1ca0203eadfddf70b0fc64a2ead2423f7beed83c 100644 --- a/source/ServerHandlers/StaticFileRequest.cs +++ b/source/ServerHandlers/StaticFileRequest.cs @@ -24,10 +24,8 @@ public class StaticFileRequest : IServerHandlers /// public bool Check(string requestPath) { - if (requestPath is null) - { - throw new ArgumentNullException(nameof(requestPath)); - } + ArgumentNullException.ThrowIfNull(requestPath); + var fileAbsolutePath = Path.Combine(basePath, requestPath.TrimStart('/')); return File.Exists(fileAbsolutePath); } @@ -35,20 +33,14 @@ public class StaticFileRequest : IServerHandlers /// public async Task Handle(IHttpListenerResponse response, string requestPath, DateTime serverStartTime) { - if (requestPath is null) - { - throw new ArgumentNullException(nameof(requestPath)); - } - if (response is null) - { - throw new ArgumentNullException(nameof(response)); - } + ArgumentNullException.ThrowIfNull(requestPath); + ArgumentNullException.ThrowIfNull(response); var fileAbsolutePath = Path.Combine(basePath, requestPath.TrimStart('/')); response.ContentType = GetContentType(fileAbsolutePath!); await using var fileStream = new FileStream(fileAbsolutePath!, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); response.ContentLength64 = fileStream.Length; - await fileStream.CopyToAsync(response.OutputStream); + await fileStream.CopyToAsync(response.OutputStream).ConfigureAwait(false); return inTheme ? "themeSt" : "static"; } diff --git a/test/BaseGeneratorCommandTests.cs b/test/BaseGeneratorCommandTests.cs index 475efd4ca3efd90762ba26cf59b129623f709901..e4f468189da6c08362d8f9fe54e5796d1834f767 100644 --- a/test/BaseGeneratorCommandTests.cs +++ b/test/BaseGeneratorCommandTests.cs @@ -24,13 +24,13 @@ public class BaseGeneratorCommandTests [Fact] public void Constructor_ShouldThrowArgumentNullException_WhenOptionsIsNull() { - Assert.Throws(() => new BaseGeneratorCommandStub(null!, testLogger)); + _ = Assert.Throws(() => new BaseGeneratorCommandStub(null!, testLogger)); } [Fact] public void Constructor_ShouldThrowArgumentNullException_WhenLoggerIsNull() { - Assert.Throws(() => new BaseGeneratorCommandStub(testOptions, null!)); + _ = Assert.Throws(() => new BaseGeneratorCommandStub(testOptions, null!)); } [Fact] diff --git a/test/Models/PageTests.cs b/test/Models/PageTests.cs index 29d87f03af527b680a59a2bf890de019d50f3f90..f254da7b249a0544edee8fed043091ff1098bcf3 100644 --- a/test/Models/PageTests.cs +++ b/test/Models/PageTests.cs @@ -89,7 +89,7 @@ word03 word04 word05 6 7 eight // Assert Assert.Equal(3, site.OutputReferences.Count); - site.OutputReferences.TryGetValue(url, out var pageOther); + _ = site.OutputReferences.TryGetValue(url, out var pageOther); Assert.NotNull(pageOther); Assert.Same(page, pageOther); } @@ -179,7 +179,7 @@ word03 word04 word05 6 7 eight }, site); var options = Substitute.For(); - options.Draft.Returns(draftOption); + _ = options.Draft.Returns(draftOption); // Assert Assert.Equal(expectedValue, site.IsValidPage(page, options)); @@ -199,7 +199,7 @@ word03 word04 word05 6 7 eight // Act var options = Substitute.For(); - options.Future.Returns(futureOption); + _ = options.Future.Returns(futureOption); // Assert Assert.Equal(expected, site.IsValidDate(page, options)); diff --git a/test/Models/SiteTests.cs b/test/Models/SiteTests.cs index f1ff2cb408550e4950b98cb33c2e89adc168b4cb..e328aba3e28dd2036ec82b82603248f6de448ffb 100644 --- a/test/Models/SiteTests.cs +++ b/test/Models/SiteTests.cs @@ -46,7 +46,7 @@ public class SiteTests : TestSetup // Assert Assert.NotNull(site.Home); Assert.True(site.Home.IsHome); - Assert.Single(site.OutputReferences.Values.Where(output => output is IPage page && page.IsHome)); + _ = Assert.Single(site.OutputReferences.Values.Where(output => output is IPage page && page.IsHome)); } [Theory] @@ -154,8 +154,8 @@ public class SiteTests : TestSetup // Act site.ParseAndScanSourceFiles(null); - // Assert - site.OutputReferences.TryGetValue("/tags", out var output); + // Assert + _ = site.OutputReferences.TryGetValue("/tags", out var output); var tagSectionPage = output as IPage; Assert.NotNull(tagSectionPage); Assert.Equal(2, tagSectionPage.Pages.Count()); @@ -177,8 +177,8 @@ public class SiteTests : TestSetup // Act site.ParseAndScanSourceFiles(null); - // Assert - site.OutputReferences.TryGetValue("/tags/tag1", out var output); + // Assert + _ = site.OutputReferences.TryGetValue("/tags/tag1", out var output); var page = output as IPage; Assert.NotNull(page); Assert.Equal(10, page.Pages.Count()); @@ -202,8 +202,8 @@ public class SiteTests : TestSetup // Act site.ParseAndScanSourceFiles(null); - // Assert - site.OutputReferences.TryGetValue(url, out var output); + // Assert + _ = site.OutputReferences.TryGetValue(url, out var output); var page = output as IPage; Assert.NotNull(page); Assert.Equal(expectedContent, page.Content); @@ -237,8 +237,8 @@ public class SiteTests : TestSetup // Act site.ParseAndScanSourceFiles(null); - // Assert - site.OutputReferences.TryGetValue(url, out var output); + // Assert + _ = site.OutputReferences.TryGetValue(url, out var output); var page = output as IPage; Assert.NotNull(page); Assert.Equal(expectedContentPreRendered, page.ContentPreRendered); @@ -263,8 +263,8 @@ public class SiteTests : TestSetup // Act site.ParseAndScanSourceFiles(null); - // Assert - site.OutputReferences.TryGetValue(url, out var output); + // Assert + _ = site.OutputReferences.TryGetValue(url, out var output); var page = output as IPage; Assert.NotNull(page); Assert.Equal(string.Empty, page.Content); @@ -303,8 +303,8 @@ public class SiteTests : TestSetup // Act site.ParseAndScanSourceFiles(null); - // Assert - site.OutputReferences.TryGetValue(url, out var output); + // Assert + _ = site.OutputReferences.TryGetValue(url, out var output); var page = output as IPage; Assert.NotNull(page); Assert.Equal(expectedContentPreRendered, page.ContentPreRendered); diff --git a/test/Parser/YAMLParserTests.cs b/test/Parser/YAMLParserTests.cs index b7372211b9789cd9f4eb1f45f6bd633a1e7369ee..2782a37295f0b2a8925c5c425ec00b3b7ffd75f5 100644 --- a/test/Parser/YAMLParserTests.cs +++ b/test/Parser/YAMLParserTests.cs @@ -135,8 +135,8 @@ Title --- "; - // Asset - Assert.Throws(() => parser.ParseFrontmatterAndMarkdown(fileRelativePathCONST, fileFullPathCONST, fileContent)); + // Asset + _ = Assert.Throws(() => parser.ParseFrontmatterAndMarkdown(fileRelativePathCONST, fileFullPathCONST, fileContent)); } [Fact] @@ -212,37 +212,37 @@ Title [Fact] public void ParseFrontmatter_ShouldThrowExceptionWhenSiteIsNull() { - Assert.Throws(() => parser.ParseFrontmatterAndMarkdownFromFile(null!, "fakeFilePath")); + _ = Assert.Throws(() => parser.ParseFrontmatterAndMarkdownFromFile(null!, "fakeFilePath")); } [Fact] public void ParseFrontmatter_ShouldThrowExceptionWhenFilePathIsNull() { - Assert.Throws(() => parser.ParseFrontmatterAndMarkdownFromFile(null!)); + _ = Assert.Throws(() => parser.ParseFrontmatterAndMarkdownFromFile(null!)); } [Fact] public void ParseFrontmatter_ShouldThrowExceptionWhenFilePathDoesNotExist() { - Assert.Throws(() => parser.ParseFrontmatterAndMarkdownFromFile("fakePath")); + _ = Assert.Throws(() => parser.ParseFrontmatterAndMarkdownFromFile("fakePath")); } [Fact] public void ParseFrontmatter_ShouldThrowExceptionWhenFilePathDoesNotExist2() { - Assert.Throws(() => parser.ParseFrontmatterAndMarkdown(null!, null!, "fakeContent")); + _ = Assert.Throws(() => parser.ParseFrontmatterAndMarkdown(null!, null!, "fakeContent")); } [Fact] public void ParseFrontmatter_ShouldHandleEmptyFileContent() { - Assert.Throws(() => parser.ParseFrontmatterAndMarkdown("fakeFilePath", "/fakeFilePath", "")); + _ = Assert.Throws(() => parser.ParseFrontmatterAndMarkdown("fakeFilePath", "/fakeFilePath", "")); } [Fact] public void ParseYAML_ShouldThrowExceptionWhenFrontmatterIsInvalid() { - Assert.Throws(() => parser.ParseFrontmatterAndMarkdown("fakeFilePath", "/fakeFilePath", "invalidFrontmatter")); + _ = Assert.Throws(() => parser.ParseFrontmatterAndMarkdown("fakeFilePath", "/fakeFilePath", "invalidFrontmatter")); } [Fact] @@ -265,13 +265,13 @@ Title [Fact] public void SiteParams_ShouldThrowExceptionWhenSettingsIsNull() { - Assert.Throws(() => parser.ParseParams(null!, typeof(Site), siteContentCONST)); + _ = Assert.Throws(() => parser.ParseParams(null!, typeof(Site), siteContentCONST)); } [Fact] public void SiteParams_ShouldThrowExceptionWhenTypeIsNull() { - Assert.Throws(() => parser.ParseParams(site, null!, siteContentCONST)); + _ = Assert.Throws(() => parser.ParseParams(site, null!, siteContentCONST)); } [Fact] diff --git a/test/ServerHandlers/PingRequestHandlerTests.cs b/test/ServerHandlers/PingRequestHandlerTests.cs index 1626c4acb255f465e44c6a55c4975e1637cba28d..d4501856398accbbb8e1ad400f113a2c86ac4d3b 100644 --- a/test/ServerHandlers/PingRequestHandlerTests.cs +++ b/test/ServerHandlers/PingRequestHandlerTests.cs @@ -12,17 +12,17 @@ public class PingRequestHandlerTests : TestSetup // Arrange var response = Substitute.For(); var stream = new MemoryStream(); - response.OutputStream.Returns(stream); + _ = response.OutputStream.Returns(stream); var pingRequests = new PingRequests(); // Act - var code = await pingRequests.Handle(response, "ping", todayDate); + var code = await pingRequests.Handle(response, "ping", todayDate).ConfigureAwait(true); - // Assert - stream.Seek(0, SeekOrigin.Begin); + // Assert + _ = stream.Seek(0, SeekOrigin.Begin); using var reader = new StreamReader(stream); - var content = await reader.ReadToEndAsync(); + var content = await reader.ReadToEndAsync().ConfigureAwait(true); Assert.Equal(todayDate.ToString("o"), content); diff --git a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs index 495e84522f17ef8577af90b850b57c8fc2279b49..21fbf14145f87537890cf78bcb02dc7a17cc7512 100644 --- a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs +++ b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs @@ -42,17 +42,17 @@ public class RegisteredPageRequestHandlerTests : TestSetup var response = Substitute.For(); var stream = new MemoryStream(); - response.OutputStream.Returns(stream); + _ = response.OutputStream.Returns(stream); // Act site.ParseAndScanSourceFiles(Path.Combine(siteFullPath, "content")); - registeredPageRequest.Check(requestPath); - var code = await registeredPageRequest.Handle(response, requestPath, DateTime.Now); + _ = registeredPageRequest.Check(requestPath); + var code = await registeredPageRequest.Handle(response, requestPath, DateTime.Now).ConfigureAwait(true); - // Assert - stream.Seek(0, SeekOrigin.Begin); + // Assert + _ = stream.Seek(0, SeekOrigin.Begin); using var reader = new StreamReader(stream); - var content = await reader.ReadToEndAsync(); + var content = await reader.ReadToEndAsync().ConfigureAwait(true); Assert.Equal("dict", code); diff --git a/test/ServerHandlers/StaticFileRequestHandlerTests.cs b/test/ServerHandlers/StaticFileRequestHandlerTests.cs index c656a0c2061600c1db959f9f7f81af3c078a2fd4..92378e70d30be23aa990515ee0733e6dcaf01506 100644 --- a/test/ServerHandlers/StaticFileRequestHandlerTests.cs +++ b/test/ServerHandlers/StaticFileRequestHandlerTests.cs @@ -44,16 +44,16 @@ public class StaticFileRequestHandlerTests : TestSetup, IDisposable var response = Substitute.For(); var stream = new MemoryStream(); - response.OutputStream.Returns(stream); + _ = response.OutputStream.Returns(stream); - // Act - staticFileRequest.Check(requestPath); - var code = await staticFileRequest.Handle(response, requestPath, DateTime.Now); + // Act + _ = staticFileRequest.Check(requestPath); + var code = await staticFileRequest.Handle(response, requestPath, DateTime.Now).ConfigureAwait(true); - // Assert - stream.Seek(0, SeekOrigin.Begin); + // Assert + _ = stream.Seek(0, SeekOrigin.Begin); using var reader = new StreamReader(stream); - var content = await reader.ReadToEndAsync(); + var content = await reader.ReadToEndAsync().ConfigureAwait(true); Assert.Equal("test", content); diff --git a/test/TestSetup.cs b/test/TestSetup.cs index ac0602ecc4951185bf75bd6a22e70a85c5b8242f..aa0f4d08a01b56a23e6672fa632213e273653b2b 100644 --- a/test/TestSetup.cs +++ b/test/TestSetup.cs @@ -42,7 +42,7 @@ public class TestSetup public TestSetup() { - systemClockMock.Now.Returns(todayDate); + _ = systemClockMock.Now.Returns(todayDate); site = new Site(generateOptionsMock, siteSettingsMock, frontMatterParser, loggerMock, systemClockMock); } }