From cddc562b50d6a246a0adba76656fb1feca3f4334 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Wed, 3 Jul 2024 18:58:09 -0700 Subject: [PATCH 1/2] Support events. --- .../CodeGeneratorApp.cs | 10 +++--- .../CodeGen/FileGenerator.cs | 1 + .../Facility.Definition.csproj | 4 +++ src/Facility.Definition/Fsd/FsdGenerator.cs | 12 ++++++- src/Facility.Definition/Fsd/FsdParser.cs | 34 +++++++++++++++++++ .../Fsd/FsdParserSettings.cs | 12 +++++++ src/Facility.Definition/Fsd/FsdParsers.cs | 4 +-- .../Http/HttpMethodInfo.cs | 9 +++++ .../Http/HttpServiceInfo.cs | 20 ++++++++--- .../ServiceDefinitionUtility.cs | 7 ++++ src/Facility.Definition/ServiceInfo.cs | 17 ++++++++-- src/Facility.Definition/ServiceMethodInfo.cs | 14 ++++++++ src/Facility.Definition/ServiceMethodKind.cs | 17 ++++++++++ src/fsdgenfsd/FsdGenFsdApp.cs | 2 ++ .../Http/HttpMethodInfoTests.cs | 3 +- .../Http/HttpServiceInfoTests.cs | 13 +++++++ .../MethodTests.cs | 13 ++++--- .../ServiceTests.cs | 6 ++-- .../TestUtility.cs | 6 ++-- 19 files changed, 176 insertions(+), 28 deletions(-) create mode 100644 src/Facility.Definition/Fsd/FsdParserSettings.cs create mode 100644 src/Facility.Definition/ServiceMethodKind.cs diff --git a/src/Facility.CodeGen.Console/CodeGeneratorApp.cs b/src/Facility.CodeGen.Console/CodeGeneratorApp.cs index 6bb3271..1db22ee 100644 --- a/src/Facility.CodeGen.Console/CodeGeneratorApp.cs +++ b/src/Facility.CodeGen.Console/CodeGeneratorApp.cs @@ -101,6 +101,11 @@ public int Run(IReadOnlyList args) /// protected virtual IReadOnlyList ExtraUsage => []; + /// + /// Creates the service parser. + /// + protected abstract ServiceParser CreateParser(); + /// /// Creates the code generator. /// @@ -113,11 +118,6 @@ public int Run(IReadOnlyList args) /// The file generator settings. protected abstract FileGeneratorSettings CreateSettings(ArgsReader args); - /// - /// Creates the service parser. - /// - protected virtual ServiceParser CreateParser() => new FsdParser(); - private void WriteUsage(CodeGenerator generator) { System.Console.WriteLine($"Usage: {generator.GeneratorName} input output [options]"); diff --git a/src/Facility.Definition/CodeGen/FileGenerator.cs b/src/Facility.Definition/CodeGen/FileGenerator.cs index c5d8c63..2c05f7f 100644 --- a/src/Facility.Definition/CodeGen/FileGenerator.cs +++ b/src/Facility.Definition/CodeGen/FileGenerator.cs @@ -13,6 +13,7 @@ public static class FileGenerator /// The code generator. /// The settings. /// The number of updated files. + [Obsolete("Use the overload that takes a parser.")] public static int GenerateFiles(CodeGenerator generator, FileGeneratorSettings settings) => GenerateFiles(new FsdParser(), generator, settings); /// diff --git a/src/Facility.Definition/Facility.Definition.csproj b/src/Facility.Definition/Facility.Definition.csproj index c785a7a..04d390d 100644 --- a/src/Facility.Definition/Facility.Definition.csproj +++ b/src/Facility.Definition/Facility.Definition.csproj @@ -8,6 +8,10 @@ README.md + + + + diff --git a/src/Facility.Definition/Fsd/FsdGenerator.cs b/src/Facility.Definition/Fsd/FsdGenerator.cs index 058f729..dac19b5 100644 --- a/src/Facility.Definition/Fsd/FsdGenerator.cs +++ b/src/Facility.Definition/Fsd/FsdGenerator.cs @@ -11,8 +11,18 @@ public sealed class FsdGenerator : CodeGenerator /// /// Generates an FSD file for a service definition. /// + /// The service parser. /// The settings. /// The number of updated files. + public static int GenerateFsd(ServiceParser parser, FsdGeneratorSettings settings) => + FileGenerator.GenerateFiles(parser, new FsdGenerator { GeneratorName = nameof(FsdGenerator) }, settings); + + /// + /// Generates an FSD file for a service definition. + /// + /// The settings. + /// The number of updated files. + [Obsolete("Use the overload that takes a parser.")] public static int GenerateFsd(FsdGeneratorSettings settings) => FileGenerator.GenerateFiles(new FsdGenerator { GeneratorName = nameof(FsdGenerator) }, settings); @@ -52,7 +62,7 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) if (member is ServiceMethodInfo method) { WriteSummaryAndAttributes(code, method); - code.WriteLine($"method {method.Name}"); + code.WriteLine($"{method.Kind.GetKeyword()} {method.Name}"); using (code.Block("{", "}:")) WriteFields(code, method.RequestFields); using (code.Block()) diff --git a/src/Facility.Definition/Fsd/FsdParser.cs b/src/Facility.Definition/Fsd/FsdParser.cs index 4fc1a3a..0c886cd 100644 --- a/src/Facility.Definition/Fsd/FsdParser.cs +++ b/src/Facility.Definition/Fsd/FsdParser.cs @@ -8,6 +8,22 @@ namespace Facility.Definition.Fsd; /// public sealed class FsdParser : ServiceParser { + /// + /// Creates an FSD parser. + /// + public FsdParser(FsdParserSettings settings) + { + m_supportsEvents = settings.SupportsEvents; + } + + /// + /// Creates an FSD parser. + /// + [Obsolete("Use the constructor that takes FsdParserSettings.")] + public FsdParser() + { + } + /// /// Implements TryParseDefinition. /// @@ -63,6 +79,22 @@ protected override bool TryParseDefinitionCore(ServiceDefinitionText text, out S : [.. targetMember.Remarks, .. s_oneEmptyString, .. remarksSection.Lines]; } } + + foreach (var method in service.AllMethods) + { + switch (method.Kind) + { + case ServiceMethodKind.Normal: + break; + case ServiceMethodKind.Event: + if (!m_supportsEvents) + errorList.Add(new ServiceDefinitionError("Events are not supported by this parser.", method.GetPart(ServicePartKind.Keyword)!.Position)); + break; + default: + errorList.Add(new ServiceDefinitionError("Unexpected method kind.", method.GetPart(ServicePartKind.Keyword)!.Position)); + break; + } + } } catch (ParseException exception) { @@ -201,4 +233,6 @@ private static void ReadRemarksAfterDefinition(ServiceDefinitionText source, Lis private static readonly Regex s_interleavedMarkdown = new(@"^```fsd\b", RegexOptions.Multiline); private static readonly Regex s_markdownHeading = new(@"^#\s+"); private static readonly string[] s_oneEmptyString = [""]; + + private readonly bool m_supportsEvents; } diff --git a/src/Facility.Definition/Fsd/FsdParserSettings.cs b/src/Facility.Definition/Fsd/FsdParserSettings.cs new file mode 100644 index 0000000..94cd799 --- /dev/null +++ b/src/Facility.Definition/Fsd/FsdParserSettings.cs @@ -0,0 +1,12 @@ +namespace Facility.Definition.Fsd; + +/// +/// Settings for parsing an FSD file. +/// +public sealed class FsdParserSettings +{ + /// + /// True to allow events. + /// + public bool SupportsEvents { get; set; } +} diff --git a/src/Facility.Definition/Fsd/FsdParsers.cs b/src/Facility.Definition/Fsd/FsdParsers.cs index 9e9ef34..15bb857 100644 --- a/src/Facility.Definition/Fsd/FsdParsers.cs +++ b/src/Facility.Definition/Fsd/FsdParsers.cs @@ -173,12 +173,12 @@ private static IParser MethodParser(Context context) => from comments1 in CommentOrWhiteSpaceParser.Many() from attributes in AttributeParser(context).Delimited(",").Bracketed("[", "]").Many() from comments2 in CommentOrWhiteSpaceParser.Many() - from keyword in KeywordParser("method") + from keyword in KeywordParser("method", "event") from name in NameParser.Named("method name") from requestFields in FieldParser(context).Many().Bracketed("{", "}") from colon in PunctuationParser(":") from responseFields in FieldParser(context).Many().Bracketed("{", "}") - select new ServiceMethodInfo(name.Value, requestFields, responseFields, + select new ServiceMethodInfo(keyword.Value == "event" ? ServiceMethodKind.Event : ServiceMethodKind.Normal, name.Value, requestFields, responseFields, attributes.SelectMany(x => x), BuildSummary(comments1, comments2), context.GetRemarksSection(name.Value)?.Lines, diff --git a/src/Facility.Definition/Http/HttpMethodInfo.cs b/src/Facility.Definition/Http/HttpMethodInfo.cs index 4b0668f..9d95b94 100644 --- a/src/Facility.Definition/Http/HttpMethodInfo.cs +++ b/src/Facility.Definition/Http/HttpMethodInfo.cs @@ -72,6 +72,7 @@ internal HttpMethodInfo(ServiceMethodInfo methodInfo, ServiceInfo serviceInfo) Method = "POST"; Path = $"/{methodInfo.Name}"; HttpStatusCode? statusCode = null; + var isEvent = methodInfo.Kind == ServiceMethodKind.Event; foreach (var methodParameter in GetHttpParameters(methodInfo)) { @@ -223,6 +224,14 @@ internal HttpMethodInfo(ServiceMethodInfo methodInfo, ServiceInfo serviceInfo) ResponseHeaderFields = responseHeaderFields; ValidResponses = [.. GetValidResponses(serviceInfo, statusCode, responseNormalFields, responseBodyFields).OrderBy(x => x.StatusCode)]; + if (isEvent) + { + if (responseHeaderFields.Count != 0) + AddValidationError(new ServiceDefinitionError("Events do not support response headers.", methodInfo.Position)); + if (ValidResponses.Count != 1) + AddValidationError(new ServiceDefinitionError("Events do not support multiple status codes.", methodInfo.Position)); + } + var duplicateStatusCode = ValidResponses.GroupBy(x => x.StatusCode).FirstOrDefault(x => x.Count() > 1); if (duplicateStatusCode is not null) AddValidationError(new ServiceDefinitionError($"Multiple handlers for status code {(int) duplicateStatusCode.Key}.", methodInfo.Position)); diff --git a/src/Facility.Definition/Http/HttpServiceInfo.cs b/src/Facility.Definition/Http/HttpServiceInfo.cs index 4894717..88fc86e 100644 --- a/src/Facility.Definition/Http/HttpServiceInfo.cs +++ b/src/Facility.Definition/Http/HttpServiceInfo.cs @@ -35,9 +35,19 @@ public static bool TryCreate(ServiceInfo serviceInfo, out HttpServiceInfo httpSe public string? Url { get; } /// - /// The HTTP mapping for the methods. + /// The HTTP mapping for all methods (normal methods and event methods). /// - public IReadOnlyList Methods { get; } + public IReadOnlyList AllMethods { get; } + + /// + /// The HTTP mapping for normal methods. + /// + public IReadOnlyList Methods => AllMethods.Where(x => x.ServiceMethod.Kind == ServiceMethodKind.Normal).ToList(); + + /// + /// The HTTP mapping for event methods. + /// + public IReadOnlyList Events => AllMethods.Where(x => x.ServiceMethod.Kind == ServiceMethodKind.Event).ToList(); /// /// The HTTP mapping for the error sets. @@ -47,7 +57,7 @@ public static bool TryCreate(ServiceInfo serviceInfo, out HttpServiceInfo httpSe /// /// The children of the element, if any. /// - public override IEnumerable GetChildren() => Methods.AsEnumerable().Concat(ErrorSets); + public override IEnumerable GetChildren() => AllMethods.AsEnumerable().Concat(ErrorSets); private HttpServiceInfo(ServiceInfo serviceInfo) { @@ -73,10 +83,10 @@ private HttpServiceInfo(ServiceInfo serviceInfo) } } - Methods = serviceInfo.Methods.Select(x => new HttpMethodInfo(x, serviceInfo)).ToList(); + AllMethods = serviceInfo.AllMethods.Select(x => new HttpMethodInfo(x, serviceInfo)).ToList(); ErrorSets = serviceInfo.ErrorSets.Select(x => new HttpErrorSetInfo(x)).ToList(); - var methodsByRoute = Methods.OrderBy(x => x, HttpMethodInfo.ByRouteComparer).ToList(); + var methodsByRoute = AllMethods.OrderBy(x => x, HttpMethodInfo.ByRouteComparer).ToList(); for (var index = 1; index < methodsByRoute.Count; index++) { var left = methodsByRoute[index - 1]; diff --git a/src/Facility.Definition/ServiceDefinitionUtility.cs b/src/Facility.Definition/ServiceDefinitionUtility.cs index 8675395..4b536e7 100644 --- a/src/Facility.Definition/ServiceDefinitionUtility.cs +++ b/src/Facility.Definition/ServiceDefinitionUtility.cs @@ -40,6 +40,13 @@ internal static ServiceDefinitionError CreateInvalidAttributeValueError(string a /// public static bool IsValidName(string? name) => name is not null && s_validNameRegex.IsMatch(name); + internal static string GetKeyword(this ServiceMethodKind kind) => kind switch + { + ServiceMethodKind.Normal => "method", + ServiceMethodKind.Event => "event", + _ => throw new ArgumentOutOfRangeException(nameof(kind)), + }; + internal static IReadOnlyList ToReadOnlyList(this IEnumerable? items) => new ReadOnlyCollection((items ?? []).ToList()); #if NET6_0_OR_GREATER diff --git a/src/Facility.Definition/ServiceInfo.cs b/src/Facility.Definition/ServiceInfo.cs index 76fc7da..f969603 100644 --- a/src/Facility.Definition/ServiceInfo.cs +++ b/src/Facility.Definition/ServiceInfo.cs @@ -43,14 +43,24 @@ public ServiceInfo(string name, IEnumerable members, IEnumera } /// - /// All of the service members.. + /// All service members. /// public IReadOnlyList Members { get; } /// - /// The methods. + /// All methods (normal methods and event methods). /// - public IReadOnlyList Methods => Members.OfType().ToReadOnlyList(); + public IReadOnlyList AllMethods => Members.OfType().ToReadOnlyList(); + + /// + /// The normal methods. + /// + public IReadOnlyList Methods => Members.OfType().Where(x => x.Kind == ServiceMethodKind.Normal).ToReadOnlyList(); + + /// + /// The event methods. + /// + public IReadOnlyList Events => Members.OfType().Where(x => x.Kind == ServiceMethodKind.Event).ToReadOnlyList(); /// /// The DTOs. @@ -125,6 +135,7 @@ ServiceMemberInfo DoExcludeTag(ServiceMemberInfo member) if (member is ServiceMethodInfo method) { return new ServiceMethodInfo( + kind: method.Kind, name: method.Name, requestFields: method.RequestFields.Where(ShouldNotExclude), responseFields: method.ResponseFields.Where(ShouldNotExclude), diff --git a/src/Facility.Definition/ServiceMethodInfo.cs b/src/Facility.Definition/ServiceMethodInfo.cs index 067bc33..9fa44b9 100644 --- a/src/Facility.Definition/ServiceMethodInfo.cs +++ b/src/Facility.Definition/ServiceMethodInfo.cs @@ -9,8 +9,17 @@ public sealed class ServiceMethodInfo : ServiceMemberInfo /// Creates a method. /// public ServiceMethodInfo(string name, IEnumerable? requestFields = null, IEnumerable? responseFields = null, IEnumerable? attributes = null, string? summary = null, IEnumerable? remarks = null, params ServicePart[] parts) + : this(ServiceMethodKind.Normal, name, requestFields, responseFields, attributes, summary, remarks, parts) + { + } + + /// + /// Creates a method. + /// + public ServiceMethodInfo(ServiceMethodKind kind, string name, IEnumerable? requestFields = null, IEnumerable? responseFields = null, IEnumerable? attributes = null, string? summary = null, IEnumerable? remarks = null, params ServicePart[] parts) : base(name, attributes, summary, remarks, parts) { + Kind = kind; RequestFields = requestFields.ToReadOnlyList(); ResponseFields = responseFields.ToReadOnlyList(); @@ -18,6 +27,11 @@ public ServiceMethodInfo(string name, IEnumerable? requestFiel ValidateNoDuplicateNames(ResponseFields, "response field"); } + /// + /// The kind of the method. + /// + public ServiceMethodKind Kind { get; } + /// /// The request fields of the method. /// diff --git a/src/Facility.Definition/ServiceMethodKind.cs b/src/Facility.Definition/ServiceMethodKind.cs new file mode 100644 index 0000000..6ec8282 --- /dev/null +++ b/src/Facility.Definition/ServiceMethodKind.cs @@ -0,0 +1,17 @@ +namespace Facility.Definition; + +/// +/// The kind of service method. +/// +public enum ServiceMethodKind +{ + /// + /// A normal method. + /// + Normal, + + /// + /// An event, i.e. a method that can indefinitely and repeatedly provide responses. + /// + Event, +} diff --git a/src/fsdgenfsd/FsdGenFsdApp.cs b/src/fsdgenfsd/FsdGenFsdApp.cs index f08ac72..c500ac6 100644 --- a/src/fsdgenfsd/FsdGenFsdApp.cs +++ b/src/fsdgenfsd/FsdGenFsdApp.cs @@ -20,6 +20,8 @@ public sealed class FsdGenFsdApp : CodeGeneratorApp " Generate file-scoped service.", ]; + protected override ServiceParser CreateParser() => new FsdParser(new FsdParserSettings { SupportsEvents = true }); + protected override CodeGenerator CreateGenerator() => new FsdGenerator(); protected override FileGeneratorSettings CreateSettings(ArgsReader args) => diff --git a/tests/Facility.Definition.UnitTests/Http/HttpMethodInfoTests.cs b/tests/Facility.Definition.UnitTests/Http/HttpMethodInfoTests.cs index 3dae1b2..5a745c4 100644 --- a/tests/Facility.Definition.UnitTests/Http/HttpMethodInfoTests.cs +++ b/tests/Facility.Definition.UnitTests/Http/HttpMethodInfoTests.cs @@ -1,6 +1,5 @@ using System.Globalization; using System.Net; -using Facility.Definition.Fsd; using Facility.Definition.Http; using FluentAssertions; using NUnit.Framework; @@ -675,7 +674,7 @@ public void NonSimpleFieldTypeNotSupported(string type) public void ByRouteComparer(string leftHttp, string rightHttp, int expected) { var fsdText = "service TestApi { [left] method left { id: string; }: {} [right] method right { id: string; }: {} }".Replace("[left]", leftHttp).Replace("[right]", rightHttp); - var service = HttpServiceInfo.Create(new FsdParser().ParseDefinition(new ServiceDefinitionText("", fsdText))); + var service = HttpServiceInfo.Create(TestUtility.CreateParser().ParseDefinition(new ServiceDefinitionText("", fsdText))); var left = service.Methods.Single(x => x.ServiceMethod.Name == "left"); var right = service.Methods.Single(x => x.ServiceMethod.Name == "right"); var actual = HttpMethodInfo.ByRouteComparer.Compare(left, right); diff --git a/tests/Facility.Definition.UnitTests/Http/HttpServiceInfoTests.cs b/tests/Facility.Definition.UnitTests/Http/HttpServiceInfoTests.cs index ec3623e..689ec52 100644 --- a/tests/Facility.Definition.UnitTests/Http/HttpServiceInfoTests.cs +++ b/tests/Facility.Definition.UnitTests/Http/HttpServiceInfoTests.cs @@ -13,6 +13,7 @@ public void EmptyServiceDefinition() info.Service.Name.Should().Be("TestApi"); info.Url.Should().BeNull(); info.Methods.Count.Should().Be(0); + info.Events.Count.Should().Be(0); info.ErrorSets.Count.Should().Be(0); } @@ -23,6 +24,18 @@ public void OneMinimalMethod() info.Service.Name.Should().Be("TestApi"); info.Url.Should().BeNull(); info.Methods.Count.Should().Be(1); + info.Events.Count.Should().Be(0); + info.ErrorSets.Count.Should().Be(0); + } + + [Test] + public void OneMinimalEvent() + { + var info = ParseHttpApi("service TestApi { event do {}: {} }"); + info.Service.Name.Should().Be("TestApi"); + info.Url.Should().BeNull(); + info.Methods.Count.Should().Be(0); + info.Events.Count.Should().Be(1); info.ErrorSets.Count.Should().Be(0); } diff --git a/tests/Facility.Definition.UnitTests/MethodTests.cs b/tests/Facility.Definition.UnitTests/MethodTests.cs index e2e0102..d9528aa 100644 --- a/tests/Facility.Definition.UnitTests/MethodTests.cs +++ b/tests/Facility.Definition.UnitTests/MethodTests.cs @@ -23,12 +23,15 @@ public void DuplicateField(bool isRequest) new ServiceMethodInfo(name: "x", requestFields: isRequest ? fields : null, responseFields: isRequest ? null : fields).IsValid.Should().BeFalse(); } - [Test] - public void OneMinimalMethod() + [TestCase(ServiceMethodKind.Normal)] + [TestCase(ServiceMethodKind.Event)] + public void OneMinimalMethod(ServiceMethodKind kind) { - var service = TestUtility.ParseTestApi("service TestApi { method do {}: {} }"); + var keyword = kind.GetKeyword(); + var service = TestUtility.ParseTestApi($$"""service TestApi { {{keyword}} do {}: {} }"""); - var method = service.Methods.Single(); + var method = service.AllMethods.Single(); + method.Kind.Should().Be(kind); method.Name.Should().Be("do"); method.Attributes.Count.Should().Be(0); method.Summary.Should().Be(""); @@ -41,7 +44,7 @@ public void OneMinimalMethod() "", "service TestApi", "{", - "\tmethod do", + $"\t{keyword} do", "\t{", "\t}:", "\t{", diff --git a/tests/Facility.Definition.UnitTests/ServiceTests.cs b/tests/Facility.Definition.UnitTests/ServiceTests.cs index 2068d78..274b1b3 100644 --- a/tests/Facility.Definition.UnitTests/ServiceTests.cs +++ b/tests/Facility.Definition.UnitTests/ServiceTests.cs @@ -20,7 +20,7 @@ public void EmptyServiceDefinition(string definition) service.ErrorSets.Count.Should().Be(0); service.Enums.Count.Should().Be(0); service.Dtos.Count.Should().Be(0); - service.Methods.Count.Should().Be(0); + service.AllMethods.Count.Should().Be(0); service.Summary.Should().Be(""); service.Remarks.Count.Should().Be(0); @@ -43,7 +43,7 @@ public void GenerateEmptyFileScopedService() service.ErrorSets.Count.Should().Be(0); service.Enums.Count.Should().Be(0); service.Dtos.Count.Should().Be(0); - service.Methods.Count.Should().Be(0); + service.AllMethods.Count.Should().Be(0); service.Summary.Should().Be(""); service.Remarks.Count.Should().Be(0); @@ -75,7 +75,7 @@ public void MissingServiceName() [Test] public void MissingEndBrace() { - TestUtility.ParseInvalidTestApi("service TestApi {").Message.Should().Be("TestApi.fsd(1,18): expected '}' or '[' or 'data' or 'enum' or 'errors' or 'extern' or 'method'"); + TestUtility.ParseInvalidTestApi("service TestApi {").Message.Should().Be("TestApi.fsd(1,18): expected '}' or '[' or 'data' or 'enum' or 'errors' or 'extern' or 'method' or 'event'"); } [Test] diff --git a/tests/Facility.Definition.UnitTests/TestUtility.cs b/tests/Facility.Definition.UnitTests/TestUtility.cs index 6325f57..7889fa3 100644 --- a/tests/Facility.Definition.UnitTests/TestUtility.cs +++ b/tests/Facility.Definition.UnitTests/TestUtility.cs @@ -6,7 +6,7 @@ internal static class TestUtility { public static ServiceInfo ParseTestApi(string text) { - return new FsdParser().ParseDefinition(new ServiceDefinitionText("TestApi.fsd", text)); + return CreateParser().ParseDefinition(new ServiceDefinitionText("TestApi.fsd", text)); } public static ServiceDefinitionException ParseInvalidTestApi(string text) @@ -24,7 +24,7 @@ public static ServiceDefinitionException ParseInvalidTestApi(string text) public static IReadOnlyList TryParseInvalidTestApi(string text) { - if (new FsdParser().TryParseDefinition(new ServiceDefinitionText("TestApi.fsd", text), out _, out var errors)) + if (CreateParser().TryParseDefinition(new ServiceDefinitionText("TestApi.fsd", text), out _, out var errors)) throw new InvalidOperationException("Parse did not fail."); return errors; } @@ -36,4 +36,6 @@ public static string[] GenerateFsd(ServiceInfo service, FsdGeneratorSettings? se generator.ApplySettings(settings); return generator.GenerateOutput(service).Files[0].Text.Split(Environment.NewLine); } + + public static FsdParser CreateParser() => new(new FsdParserSettings { SupportsEvents = true }); } From 2ced2d5fa48a992dec44fdbab1329a1f349bfa12 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Wed, 3 Jul 2024 18:59:44 -0700 Subject: [PATCH 2/2] Publish 2.14.0. --- Directory.Build.props | 4 ++-- ReleaseNotes.md | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9b35551..9cb9fc5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 2.13.0 - 2.12.1 + 2.14.0 + 2.13.0 12.0 enable enable diff --git a/ReleaseNotes.md b/ReleaseNotes.md index dd64659..9906b50 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,5 +1,9 @@ # Release Notes +## 2.14.0 + +* Support events. (Must opt-in via `FsdParserSettings.SupportsEvents`.) + ## 2.13.0 * Add `WriteNoIndent` and `WriteLineNoIndent` to `CodeWriter`.