Skip to content

Commit

Permalink
chore: Fix internal exception on TOC loading
Browse files Browse the repository at this point in the history
  • Loading branch information
filzrev committed Feb 18, 2025
1 parent 7d095d6 commit b14970c
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 152 deletions.
47 changes: 28 additions & 19 deletions src/Docfx.Build/TableOfContents/TocHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
using Docfx.Common;
using Docfx.DataContracts.Common;
using Docfx.Plugins;
using YamlDotNet.Core.Events;
using YamlDotNet.Core;
using Constants = Docfx.DataContracts.Common.Constants;

namespace Docfx.Build.TableOfContents;

public static class TocHelper
{
private static readonly YamlDeserializerWithFallback _deserializer =
YamlDeserializerWithFallback.Create<List<TocItemViewModel>>()
.WithFallback<TocItemViewModel>();

internal static List<FileModel> ResolveToc(ImmutableList<FileModel> models)
{
var tocCache = new Dictionary<string, TocItemInfo>(FilePathComparer.OSPlatformSensitiveStringComparer);
Expand Down Expand Up @@ -56,21 +55,20 @@ public static TocItemViewModel LoadSingleToc(string file)
var fileType = Utility.GetTocFileType(file);
try
{
if (fileType == TocFileType.Markdown)
{
return new()
{
Items = MarkdownTocReader.LoadToc(EnvironmentContext.FileAbstractLayer.ReadAllText(file), file)
};
}
else if (fileType == TocFileType.Yaml)
switch (fileType)
{
return _deserializer.Deserialize(file) switch
{
List<TocItemViewModel> vm => new() { Items = vm },
TocItemViewModel root => root,
_ => throw new NotSupportedException($"{file} is not a valid TOC file."),
};
case TocFileType.Markdown:
return new()
{
Items = MarkdownTocReader.LoadToc(EnvironmentContext.FileAbstractLayer.ReadAllText(file), file)
};
case TocFileType.Yaml:
{
var yaml = EnvironmentContext.FileAbstractLayer.ReadAllText(file);
return DeserializeYamlToc(yaml);
}
default:
throw new NotSupportedException($"{file} is not a valid TOC file, supported TOC files should be either \"{Constants.TableOfContents.MarkdownTocFileName}\" or \"{Constants.TableOfContents.YamlTocFileName}\".");
}
}
catch (Exception e)
Expand All @@ -79,7 +77,18 @@ public static TocItemViewModel LoadSingleToc(string file)
Logger.LogError(message, code: ErrorCodes.Toc.InvalidTocFile);
throw new DocumentException(message, e);
}
}

private static TocItemViewModel DeserializeYamlToc(string yaml)
{
// Parse yaml content to determine TOC type (`List<TocItemViewModel>` or TocItemViewModel).
var parser = new Parser(new Scanner(new StringReader(yaml), skipComments: true));
bool isListItems = parser.TryConsume<StreamStart>(out var _)
&& parser.TryConsume<DocumentStart>(out var _)
&& parser.TryConsume<SequenceStart>(out var _);

throw new NotSupportedException($"{file} is not a valid TOC file, supported TOC files should be either \"{Constants.TableOfContents.MarkdownTocFileName}\" or \"{Constants.TableOfContents.YamlTocFileName}\".");
return isListItems
? new TocItemViewModel { Items = YamlUtility.Deserialize<List<TocItemViewModel>>(new StringReader(yaml)) }
: YamlUtility.Deserialize<TocItemViewModel>(new StringReader(yaml));
}
}
62 changes: 0 additions & 62 deletions src/Docfx.Common/YamlDeserializerWithFallback.cs

This file was deleted.

124 changes: 124 additions & 0 deletions test/Docfx.Build.Tests/TocHelperTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using Docfx.Common;
using Docfx.DataContracts.Common;
using FluentAssertions;
using Xunit;

namespace Docfx.Build.TableOfContents.Tests;

public class TocHelperTest
{
[Fact]
public void TestItemDeserialization()
{
// Arrange
var item = new TocItemViewModel
{
Items =
[
new TocItemViewModel { Uid = "item1" },
new TocItemViewModel { Uid = "item2" }
],
};

var yaml = ToYaml(item);
var filePath = Path.Combine(Path.GetTempPath(), "toc.yml");
File.WriteAllText(filePath, yaml, new UTF8Encoding(false));

try
{
// Act
var result = TocHelper.LoadSingleToc(filePath);

// Assert
result.Should().BeEquivalentTo(item);
}
finally
{
File.Delete(filePath);
}
}

[Fact]
public void TestListDeserialization()
{
// Arrange
var items = new TocItemViewModel[]
{
new TocItemViewModel { Uid = "item1" },
new TocItemViewModel { Uid = "item2" },
};

var yaml = ToYaml(items);
var filePath = Path.Combine(Path.GetTempPath(), "toc.yml");
File.WriteAllText(filePath, yaml);

try
{
// Act
var result = TocHelper.LoadSingleToc(filePath);

// Assert
result.Uid.Should().BeNull();
result.Href.Should().BeNull();
result.Items.Should().BeEquivalentTo(items);
}
finally
{
File.Delete(filePath);
}
}

[Fact]
public void TestItemDeserializationWithEncoding()
{
// Arrange
var item = new TocItemViewModel
{
Items =
[
new TocItemViewModel { Uid = "item1" },
new TocItemViewModel { Uid = "item2" }
],
};

var yaml = ToYaml(item);

foreach (var encoding in Encodings)
{
var filePath = Path.Combine(Path.GetTempPath(), "toc.yml");
File.WriteAllText(filePath, yaml, encoding);

try
{
// Act
var result = TocHelper.LoadSingleToc(filePath);

// Assert
result.Should().BeEquivalentTo(item);
}
finally
{
File.Delete(filePath);
}
}
}

private static readonly Encoding[] Encodings =
[
new UTF8Encoding(false),
new UTF8Encoding(true),
Encoding.Unicode,
Encoding.BigEndianUnicode,
];

private static string ToYaml<T>(T model)
{
using StringWriter sw = new StringWriter();
YamlUtility.Serialize(sw, model);
return sw.ToString();
}
}
71 changes: 0 additions & 71 deletions test/Docfx.Common.Tests/YamlDeserializerWithFallbackTest.cs

This file was deleted.

0 comments on commit b14970c

Please sign in to comment.