diff --git a/src/RefDocGen/Program.cs b/src/RefDocGen/Program.cs index f3a61b71..6455021d 100644 --- a/src/RefDocGen/Program.cs +++ b/src/RefDocGen/Program.cs @@ -92,7 +92,7 @@ private static async Task Run(IProgramConfiguration config) Dictionary templateProcessors = new() { - [DocumentationTemplate.Default] = new DefaultTemplateProcessor(htmlRenderer, availableLanguages, config.StaticPagesDir, config.DocVersion) + [DocumentationTemplate.Default] = new DefaultTemplateProcessor(htmlRenderer, availableLanguages, config.StaticPagesDir, config.DocVersion, config.ForceCreate) // #ADD_TEMPLATE: use the enum value together with the RazorTemplateProcessor with 8 type parameters, representing the templates // additionally, pass the 'DocCommentHtmlConfiguration' or a custom configuration (if provided) // @@ -111,7 +111,8 @@ private static async Task Run(IProgramConfiguration config) // htmlRenderer, // availableLanguages, // config.StaticPagesDir, - // config.DocVersion) + // config.DocVersion, + // config.ForceCreate) // // #ADD_TEMPLATE_PROCESSOR: use the enum value together with the custom template processor // diff --git a/src/RefDocGen/TemplateProcessors/Default/DefaultTemplateProcessor.cs b/src/RefDocGen/TemplateProcessors/Default/DefaultTemplateProcessor.cs index da666c6f..fbaf6a62 100644 --- a/src/RefDocGen/TemplateProcessors/Default/DefaultTemplateProcessor.cs +++ b/src/RefDocGen/TemplateProcessors/Default/DefaultTemplateProcessor.cs @@ -28,15 +28,17 @@ internal class DefaultTemplateProcessor : RazorTemplateProcessor< /// Renderer of the Razor components. /// Path to the directory containing the static pages created by user. null indicates that the directory is not specified. /// Version of the documentation (e.g. 'v1.0'). Pass null if no specific version should be generated. + /// If and the already exists, the existing version will be overwritten. /// Configuration of languages available in the documentation. internal DefaultTemplateProcessor(HtmlRenderer htmlRenderer, ILanguageConfiguration[] availableLanguages, - string? staticPagesDirectory = null, string? docVersion = null) + string? staticPagesDirectory = null, string? docVersion = null, bool forceCreate = false) : base( htmlRenderer, new DocCommentTransformer(new DocCommentHtmlConfiguration()), availableLanguages, staticPagesDirectory, - docVersion) + docVersion, + forceCreate) { } } diff --git a/src/RefDocGen/TemplateProcessors/Shared/DocVersioning/DocVersionManager.cs b/src/RefDocGen/TemplateProcessors/Shared/DocVersioning/DocVersionManager.cs index ef42eaee..08dda5fd 100644 --- a/src/RefDocGen/TemplateProcessors/Shared/DocVersioning/DocVersionManager.cs +++ b/src/RefDocGen/TemplateProcessors/Shared/DocVersioning/DocVersionManager.cs @@ -41,7 +41,11 @@ internal class DocVersionManager /// /// Base output directory, containing the JSON versions file. /// Current version of the documentation being generated. - public DocVersionManager(string baseOutputDirectory, string currentVersion) + /// + /// If and a version with the same name already exists, the existing version will be overwritten. + /// If and a duplicate version is found, a is thrown. + /// + public DocVersionManager(string baseOutputDirectory, string currentVersion, bool forceCreate = false) { this.baseOutputDirectory = baseOutputDirectory; this.currentVersion = currentVersion; @@ -60,7 +64,20 @@ public DocVersionManager(string baseOutputDirectory, string currentVersion) if (versions.Any(v => v.Version == currentVersion)) { - throw new DuplicateDocVersionNameException(currentVersion); // there's already a version with the same name -> throw an exception + if (forceCreate) + { + _ = versions.RemoveAll(v => v.Version == currentVersion); // remove the existing version from the list + + string existingVersionDir = Path.Join(baseOutputDirectory, currentVersion); + if (Directory.Exists(existingVersionDir)) + { + Directory.Delete(existingVersionDir, true); // delete the existing version directory + } + } + else + { + throw new DuplicateDocVersionNameException(currentVersion); // there's already a version with the same name -> throw an exception + } } } else diff --git a/src/RefDocGen/TemplateProcessors/Shared/RazorTemplateProcessor.cs b/src/RefDocGen/TemplateProcessors/Shared/RazorTemplateProcessor.cs index dd70f398..75f9812a 100644 --- a/src/RefDocGen/TemplateProcessors/Shared/RazorTemplateProcessor.cs +++ b/src/RefDocGen/TemplateProcessors/Shared/RazorTemplateProcessor.cs @@ -114,6 +114,11 @@ internal class RazorTemplateProcessor< /// private readonly string? docVersion; + /// + /// Indicates whether an existing documentation version with the same name should be overwritten. + /// + private readonly bool forceCreate; + /// /// Set of paths of all generated pages in the current doc version, relative to . /// @@ -157,18 +162,21 @@ internal class RazorTemplateProcessor< /// Transformer of the XML doc comments into HTML. /// Path to the directory containing the static pages created by user. null indicates that the directory is not specified. /// Version of the documentation (e.g. 'v1.0'). Pass null if no specific version should be generated. + /// If and the already exists, the existing version will be overwritten. /// internal RazorTemplateProcessor( HtmlRenderer htmlRenderer, IDocCommentTransformer docCommentTransformer, IEnumerable availableLanguages, string? staticPagesDirectory = null, - string? docVersion = null) + string? docVersion = null, + bool forceCreate = false) { this.htmlRenderer = htmlRenderer; this.docCommentTransformer = docCommentTransformer; this.staticPagesDirectory = staticPagesDirectory; this.docVersion = docVersion; + this.forceCreate = forceCreate; this.availableLanguages = availableLanguages; defaultIndexPage = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "TemplateProcessors", "Shared", "StaticData", "defaultIndexPage.html"); @@ -191,10 +199,10 @@ public void ProcessTemplates(ITypeRegistry typeRegistry, string outputDirectory, { string rootOutputDirectory = outputDirectory; + versionManager = new(rootOutputDirectory, docVersion, forceCreate); + this.outputDirectory = Path.Join(outputDirectory, docVersion); // set output directory _ = Directory.CreateDirectory(this.outputDirectory); - - versionManager = new(rootOutputDirectory, docVersion); } else { @@ -569,6 +577,7 @@ private void CreateVersionedDocIndexPage() /// The configuration describing mapping of the inner XML tags into HTML. /// Path to the directory containing the static pages created by user. null indicates that the directory is not specified. /// Version of the documentation (e.g. 'v1.0'). Pass null if no specific version should be generated. + /// If and the already exists, the existing version will be overwritten. /// /// An instance of class. internal static ITemplateProcessor With( @@ -576,7 +585,8 @@ internal static ITemplateProcessor With( HtmlRenderer htmlRenderer, IEnumerable availableLanguages, string? staticPagesDirectory = null, - string? docVersion = null) + string? docVersion = null, + bool forceCreate = false) { return new RazorTemplateProcessor< TObjectTypePageTemplate, @@ -587,6 +597,6 @@ internal static ITemplateProcessor With( TApiHomePageTemplate, TStaticPageTemplate, TSearchPageTemplate - >(htmlRenderer, new DocCommentTransformer(docCommentHtmlConfiguration), availableLanguages, staticPagesDirectory, docVersion); + >(htmlRenderer, new DocCommentTransformer(docCommentHtmlConfiguration), availableLanguages, staticPagesDirectory, docVersion, forceCreate); } } diff --git a/tests/RefDocGen.UnitTests/TemplateProcessors/Shared/DocVersionManagerTests.cs b/tests/RefDocGen.UnitTests/TemplateProcessors/Shared/DocVersionManagerTests.cs new file mode 100644 index 00000000..54581ea4 --- /dev/null +++ b/tests/RefDocGen.UnitTests/TemplateProcessors/Shared/DocVersionManagerTests.cs @@ -0,0 +1,79 @@ +using RefDocGen.TemplateProcessors.Shared.DocVersioning; +using RefDocGen.Tools.Exceptions; +using Shouldly; +using System.Text.Json; + +namespace RefDocGen.UnitTests.TemplateProcessors.Shared; + +/// +/// Class containing tests for class. +/// +public class DocVersionManagerTests : IDisposable +{ + /// + /// Temporary directory used as the base output directory for tests. + /// + private readonly string tempDirectory; + + public DocVersionManagerTests() + { + tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + } + + public void Dispose() + { + Directory.Delete(tempDirectory, true); + GC.SuppressFinalize(this); + } + + [Fact] + public void Constructor_ThrowsDuplicateDocVersionNameException_WhenVersionAlreadyExistsAndForceCreateIsFalse() + { + var firstManager = new DocVersionManager(tempDirectory, "v1.0"); + firstManager.SaveCurrentVersionData([]); + + Should.Throw(() => new DocVersionManager(tempDirectory, "v1.0", forceCreate: false)); + } + + [Fact] + public void Constructor_DoesNotThrow_WhenVersionAlreadyExistsAndForceCreateIsTrue() + { + var firstManager = new DocVersionManager(tempDirectory, "v1.0"); + firstManager.SaveCurrentVersionData([]); + + Should.NotThrow(() => new DocVersionManager(tempDirectory, "v1.0", forceCreate: true)); + } + + [Fact] + public void Constructor_DeletesExistingVersionDirectory_WhenForceCreateIsTrue() + { + string versionDir = Path.Join(tempDirectory, "v1.0"); + Directory.CreateDirectory(versionDir); + File.WriteAllText(Path.Join(versionDir, "page.html"), ""); + + var firstManager = new DocVersionManager(tempDirectory, "v1.0"); + firstManager.SaveCurrentVersionData(["page.html"]); + + _ = new DocVersionManager(tempDirectory, "v1.0", forceCreate: true); + + Directory.Exists(versionDir).ShouldBeFalse(); + } + + [Fact] + public void Constructor_RemovesExistingVersionFromVersionsList_WhenForceCreateIsTrue() + { + var firstManager = new DocVersionManager(tempDirectory, "v1.0"); + firstManager.SaveCurrentVersionData([]); + + var secondManager = new DocVersionManager(tempDirectory, "v1.0", forceCreate: true); + secondManager.SaveCurrentVersionData([]); + + string json = File.ReadAllText(Path.Join(tempDirectory, "versions.json")); + var versions = JsonSerializer.Deserialize>(json); + + versions.ShouldNotBeNull(); + versions.Count.ShouldBe(1); // only one entry, not two + versions[0].GetProperty("Version").GetString().ShouldBe("v1.0"); + } +}