Public Access
Fix YAML escaping for meeting note frontmatter
PR and Push Build/Test / build-and-test (push) Successful in 10m21s
PR and Push Build/Test / build-and-test (push) Successful in 10m21s
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
@@ -113,7 +115,24 @@ public sealed class MarkdownMeetingNoteStore : IMeetingNoteStore
|
||||
|
||||
private static string EscapeQuoted(string value)
|
||||
{
|
||||
return $"\"{value.Replace("\"", "\\\"", StringComparison.Ordinal)}\"";
|
||||
var escaped = new StringBuilder(value.Length + 2);
|
||||
escaped.Append('"');
|
||||
foreach (var character in value)
|
||||
{
|
||||
escaped.Append(character switch
|
||||
{
|
||||
'\\' => "\\\\",
|
||||
'"' => "\\\"",
|
||||
'\r' => "\\r",
|
||||
'\n' => "\\n",
|
||||
'\t' => "\\t",
|
||||
< ' ' => "\\x" + ((int)character).ToString("X2", CultureInfo.InvariantCulture),
|
||||
_ => character.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
escaped.Append('"');
|
||||
return escaped.ToString();
|
||||
}
|
||||
|
||||
private static string EscapeNullableDateTime(DateTimeOffset? value)
|
||||
@@ -135,7 +154,7 @@ public sealed class MarkdownMeetingNoteStore : IMeetingNoteStore
|
||||
return "\"\"";
|
||||
}
|
||||
|
||||
if (value.Contains(':', StringComparison.Ordinal) || value.StartsWith("[", StringComparison.Ordinal))
|
||||
if (RequiresQuotedScalar(value))
|
||||
{
|
||||
return EscapeQuoted(value);
|
||||
}
|
||||
@@ -143,6 +162,49 @@ public sealed class MarkdownMeetingNoteStore : IMeetingNoteStore
|
||||
return value;
|
||||
}
|
||||
|
||||
private static bool RequiresQuotedScalar(string value)
|
||||
{
|
||||
if (char.IsWhiteSpace(value[0]) || char.IsWhiteSpace(value[^1]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.Contains(':', StringComparison.Ordinal) ||
|
||||
value.Contains('\'', StringComparison.Ordinal) ||
|
||||
value.Contains(" #", StringComparison.Ordinal) ||
|
||||
value.Any(char.IsControl))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsYamlIndicator(value[0]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsYamlCoreSchemaKeyword(value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return DateTimeOffset.TryParse(value, out _) ||
|
||||
double.TryParse(value, CultureInfo.InvariantCulture, out _);
|
||||
}
|
||||
|
||||
private static bool IsYamlIndicator(char character)
|
||||
{
|
||||
return character is '-' or '?' or ':' or ',' or '[' or ']' or '{' or '}' or
|
||||
'#' or '&' or '*' or '!' or '|' or '>' or '\'' or '"' or '%' or '@' or '`';
|
||||
}
|
||||
|
||||
private static bool IsYamlCoreSchemaKeyword(string value)
|
||||
{
|
||||
return string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(value, "false", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(value, "null", StringComparison.OrdinalIgnoreCase) ||
|
||||
value == "~";
|
||||
}
|
||||
|
||||
private sealed class MeetingNoteYaml
|
||||
{
|
||||
[YamlMember(Alias = "title")]
|
||||
|
||||
Reference in New Issue
Block a user