Skip to content

Commit c4d4389

Browse files
committed
Ensure uncaught yaml parsing errors are recoverable
1 parent b587de7 commit c4d4389

File tree

3 files changed

+90
-46
lines changed

3 files changed

+90
-46
lines changed

src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,16 @@ public virtual async Task StopAsync(CancellationToken cancellationToken)
141141
}
142142

143143

144-
public void EmitError(string file, string message)
144+
public void EmitError(string file, string message, Exception? e = null)
145145
{
146146
var d = new Diagnostic
147147
{
148148
Severity = Severity.Error,
149149
File = file,
150-
Message = message,
150+
Message = message
151+
+ (e != null ? Environment.NewLine + e : string.Empty)
152+
+ (e?.InnerException != null ? Environment.NewLine + e.InnerException : string.Empty),
153+
151154
};
152155
Channel.Write(d);
153156
}

src/Elastic.Markdown/IO/ConfigurationFile.cs

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ public record ConfigurationFile : DocumentationFile
2323
public HashSet<string> Files { get; } = new(StringComparer.OrdinalIgnoreCase);
2424
public HashSet<string> ImplicitFolders { get; } = new(StringComparer.OrdinalIgnoreCase);
2525
public Glob[] Globs { get; } = [];
26-
public HashSet<string> ExternalLinkHosts { get; } = new(StringComparer.OrdinalIgnoreCase)
27-
{
28-
"elastic.co",
29-
"github.com",
30-
};
26+
public HashSet<string> ExternalLinkHosts { get; } = new(StringComparer.OrdinalIgnoreCase) { "elastic.co", "github.com", };
3127

3228
private readonly Dictionary<string, string> _substitutions = new(StringComparer.OrdinalIgnoreCase);
3329
public IReadOnlyDictionary<string, string> Substitutions => _substitutions;
@@ -57,41 +53,49 @@ public ConfigurationFile(IFileInfo sourceFile, IDirectoryInfo rootPath, BuildCon
5753
return;
5854
}
5955

60-
// Examine the stream
61-
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
62-
63-
foreach (var entry in mapping.Children)
56+
try
6457
{
65-
var key = ((YamlScalarNode)entry.Key).Value;
66-
switch (key)
67-
{
68-
case "project":
69-
Project = ReadString(entry);
70-
break;
71-
case "exclude":
72-
Exclude = ReadStringArray(entry)
73-
.Select(Glob.Parse)
74-
.ToArray();
75-
break;
76-
case "subs":
77-
_substitutions = ReadDictionary(entry);
78-
break;
79-
case "external_hosts":
80-
var hosts = ReadStringArray(entry)
81-
.ToArray();
82-
foreach (var host in hosts)
83-
ExternalLinkHosts.Add(host);
84-
break;
85-
case "toc":
86-
var entries = ReadChildren(entry, string.Empty);
58+
// Examine the stream
59+
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
8760

88-
TableOfContents = entries;
89-
break;
90-
default:
91-
EmitWarning($"{key} is not a known configuration", entry.Key);
92-
break;
61+
foreach (var entry in mapping.Children)
62+
{
63+
var key = ((YamlScalarNode)entry.Key).Value;
64+
switch (key)
65+
{
66+
case "project":
67+
Project = ReadString(entry);
68+
break;
69+
case "exclude":
70+
Exclude = ReadStringArray(entry)
71+
.Select(Glob.Parse)
72+
.ToArray();
73+
break;
74+
case "subs":
75+
_substitutions = ReadDictionary(entry);
76+
break;
77+
case "external_hosts":
78+
var hosts = ReadStringArray(entry)
79+
.ToArray();
80+
foreach (var host in hosts)
81+
ExternalLinkHosts.Add(host);
82+
break;
83+
case "toc":
84+
var entries = ReadChildren(entry, string.Empty);
85+
86+
TableOfContents = entries;
87+
break;
88+
default:
89+
EmitWarning($"{key} is not a known configuration", entry.Key);
90+
break;
91+
}
9392
}
9493
}
94+
catch (Exception e)
95+
{
96+
EmitError("Could not load docset.yml", e);
97+
}
98+
9599
Globs = ImplicitFolders.Select(f => Glob.Parse($"{f}/*.md")).ToArray();
96100
}
97101

@@ -100,8 +104,14 @@ private List<ITocItem> ReadChildren(KeyValuePair<YamlNode, YamlNode> entry, stri
100104
var entries = new List<ITocItem>();
101105
if (entry.Value is not YamlSequenceNode sequence)
102106
{
103-
var key = ((YamlScalarNode)entry.Key).Value;
104-
EmitWarning($"'{key}' is not an array");
107+
if (entry.Key is YamlScalarNode scalarKey)
108+
{
109+
var key = scalarKey.Value;
110+
EmitWarning($"'{key}' is not an array");
111+
}
112+
else
113+
EmitWarning($"'{entry.Key}' is not an array");
114+
105115
return entries;
106116
}
107117

@@ -159,10 +169,17 @@ private Dictionary<string, string> ReadDictionary(KeyValuePair<YamlNode, YamlNod
159169
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
160170
if (entry.Value is not YamlMappingNode mapping)
161171
{
162-
var key = ((YamlScalarNode)entry.Key).Value;
163-
EmitWarning($"'{key}' is not a dictionary");
172+
if (entry.Key is YamlScalarNode scalarKey)
173+
{
174+
var key = scalarKey.Value;
175+
EmitWarning($"'{key}' is not a dictionary");
176+
}
177+
else
178+
EmitWarning($"'{entry.Key}' is not a dictionary");
179+
164180
return dictionary;
165181
}
182+
166183
foreach (var entryValue in mapping.Children)
167184
{
168185
if (entryValue.Key is not YamlScalarNode scalar || scalar.Value is null)
@@ -172,6 +189,7 @@ private Dictionary<string, string> ReadDictionary(KeyValuePair<YamlNode, YamlNod
172189
if (value is not null)
173190
dictionary.Add(key, value);
174191
}
192+
175193
return dictionary;
176194
}
177195

@@ -187,6 +205,7 @@ private Dictionary<string, string> ReadDictionary(KeyValuePair<YamlNode, YamlNod
187205
else
188206
found = true;
189207
}
208+
190209
return folder;
191210
}
192211

@@ -212,9 +231,14 @@ private Dictionary<string, string> ReadDictionary(KeyValuePair<YamlNode, YamlNod
212231
if (entry.Value is YamlScalarNode scalar)
213232
return scalar.Value;
214233

215-
var key = ((YamlScalarNode)entry.Key).Value;
234+
if (entry.Key is YamlScalarNode scalarKey)
235+
{
236+
var key = scalarKey.Value;
237+
EmitError($"'{key}' is not a string", entry.Key);
238+
return null;
239+
}
216240

217-
EmitError($"'{key}' is not a string", entry.Key);
241+
EmitError($"'{entry.Key}' is not a string", entry.Key);
218242
return null;
219243
}
220244

@@ -239,6 +263,9 @@ private void EmitError(string message, YamlNode? node) =>
239263
private void EmitWarning(string message, YamlNode? node) =>
240264
EmitWarning(message, node?.Start, node?.End, (node as YamlScalarNode)?.Value?.Length);
241265

266+
private void EmitError(string message, Exception e) =>
267+
_context.Collector.EmitError(_sourceFile.FullName, message, e);
268+
242269
private void EmitError(string message, Mark? start = null, Mark? end = null, int? length = null)
243270
{
244271
length ??= start.HasValue && end.HasValue ? (int)start.Value.Column - (int)end.Value.Column : null;
@@ -269,4 +296,3 @@ private void EmitWarning(string message, Mark? start = null, Mark? end = null, i
269296
_context.Collector.Channel.Write(d);
270297
}
271298
}
272-

src/Elastic.Markdown/IO/MarkdownFile.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public MarkdownFile(IFileInfo sourceFile, IDirectoryInfo rootPath, MarkdownParse
2323
: base(sourceFile, rootPath)
2424
{
2525
FileName = sourceFile.Name;
26+
FilePath = sourceFile.FullName;
2627
UrlPathPrefix = context.UrlPathPrefix;
2728
MarkdownParser = parser;
2829
Collector = context.Collector;
@@ -47,6 +48,7 @@ public string? NavigationTitle
4748
private readonly HashSet<string> _additionalLabels = new();
4849
public IReadOnlySet<string> AdditionalLabels => _additionalLabels;
4950

51+
public string FilePath { get; }
5052
public string FileName { get; }
5153
public string Url => $"{UrlPathPrefix}/{RelativePath.Replace(".md", ".html")}";
5254

@@ -75,7 +77,7 @@ private void ReadDocumentInstructions(MarkdownDocument document)
7577
if (document.FirstOrDefault() is YamlFrontMatterBlock yaml)
7678
{
7779
var raw = string.Join(Environment.NewLine, yaml.Lines.Lines);
78-
YamlFrontMatter = YamlSerialization.Deserialize<YamlFrontMatter>(raw);
80+
YamlFrontMatter = ReadYamlFrontMatter(document, raw);
7981
Title = YamlFrontMatter.Title;
8082
NavigationTitle = YamlFrontMatter.NavigationTitle;
8183
}
@@ -110,6 +112,19 @@ private void ReadDocumentInstructions(MarkdownDocument document)
110112
_instructionsParsed = true;
111113
}
112114

115+
private YamlFrontMatter ReadYamlFrontMatter(MarkdownDocument document, string raw)
116+
{
117+
try
118+
{
119+
return YamlSerialization.Deserialize<YamlFrontMatter>(raw);
120+
}
121+
catch (Exception e)
122+
{
123+
Collector.EmitError(FilePath, "Failed to parse yaml front matter block.", e);
124+
return new YamlFrontMatter();
125+
}
126+
}
127+
113128

114129
public string CreateHtml(MarkdownDocument document) =>
115130
// var writer = new StringWriter();

0 commit comments

Comments
 (0)