feat(batch5): implement jetstream error helpers and group01 constructors
This commit is contained in:
@@ -0,0 +1,147 @@
|
|||||||
|
// Copyright 2020-2026 The NATS Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
//
|
||||||
|
// Generated constructor surface for JetStream API errors.
|
||||||
|
// Source parity: server/jetstream_errors_generated.go
|
||||||
|
|
||||||
|
namespace ZB.MOM.NatsNet.Server;
|
||||||
|
|
||||||
|
public static partial class JsApiErrors
|
||||||
|
{
|
||||||
|
public static JsApiError NewJSAccountResourcesExceededError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(AccountResourcesExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishContainsDuplicateMessageError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(AtomicPublishContainsDuplicateMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishDisabledError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(AtomicPublishDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishIncompleteBatchError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(AtomicPublishIncompleteBatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishInvalidBatchCommitError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(AtomicPublishInvalidBatchCommit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishInvalidBatchIDError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(AtomicPublishInvalidBatchID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishMissingSeqError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(AtomicPublishMissingSeq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSBadRequestError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(BadRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSClusterIncompleteError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(ClusterIncomplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSClusterNotActiveError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(ClusterNotActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSClusterNotAssignedError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(ClusterNotAssigned);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSClusterNotAvailError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(ClusterNotAvail);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSClusterNotLeaderError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(ClusterNotLeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSClusterPeerNotMemberError(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(ClusterPeerNotMember);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishTooLargeBatchError(object? size, params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return NewWithTags(AtomicPublishTooLargeBatch, "{size}", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSAtomicPublishUnsupportedHeaderBatchError(object? header, params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return NewWithTags(AtomicPublishUnsupportedHeaderBatch, "{header}", header);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsApiError NewJSClusterNoPeersError(Exception err, params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return NewWithTags(ClusterNoPeers, "{err}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ public sealed class JsApiError
|
|||||||
/// Pre-built <see cref="JsApiError"/> instances for all JetStream error codes.
|
/// Pre-built <see cref="JsApiError"/> instances for all JetStream error codes.
|
||||||
/// Mirrors the <c>ApiErrors</c> map in server/jetstream_errors_generated.go.
|
/// Mirrors the <c>ApiErrors</c> map in server/jetstream_errors_generated.go.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class JsApiErrors
|
public static partial class JsApiErrors
|
||||||
{
|
{
|
||||||
public delegate object? ErrorOption();
|
public delegate object? ErrorOption();
|
||||||
|
|
||||||
@@ -356,14 +356,10 @@ public static class JsApiErrors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static JsApiError NewJSRestoreSubscribeFailedError(Exception err, string subject, params ErrorOption[] opts)
|
public static JsApiError NewJSRestoreSubscribeFailedError(Exception err, string subject, params ErrorOption[] opts)
|
||||||
{
|
{
|
||||||
var overridden = ParseUnless(opts);
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
if (overridden != null)
|
return Clone(overridden);
|
||||||
return overridden;
|
|
||||||
|
|
||||||
return NewWithTags(
|
return NewWithTags(RestoreSubscribeFailed, "{subject}", subject, "{err}", err);
|
||||||
RestoreSubscribeFailed,
|
|
||||||
("{err}", err.Message),
|
|
||||||
("{subject}", subject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -371,11 +367,10 @@ public static class JsApiErrors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static JsApiError NewJSStreamRestoreError(Exception err, params ErrorOption[] opts)
|
public static JsApiError NewJSStreamRestoreError(Exception err, params ErrorOption[] opts)
|
||||||
{
|
{
|
||||||
var overridden = ParseUnless(opts);
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
if (overridden != null)
|
return Clone(overridden);
|
||||||
return overridden;
|
|
||||||
|
|
||||||
return NewWithTags(StreamRestore, ("{err}", err.Message));
|
return NewWithTags(StreamRestore, "{err}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -383,20 +378,20 @@ public static class JsApiErrors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static JsApiError NewJSPeerRemapError(params ErrorOption[] opts)
|
public static JsApiError NewJSPeerRemapError(params ErrorOption[] opts)
|
||||||
{
|
{
|
||||||
var overridden = ParseUnless(opts);
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
return overridden ?? Clone(PeerRemap);
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(PeerRemap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsApiError? ParseUnless(ReadOnlySpan<ErrorOption> opts)
|
private static object? ParseOpts(params ErrorOption[] opts)
|
||||||
{
|
{
|
||||||
foreach (var opt in opts)
|
object? value = null;
|
||||||
{
|
|
||||||
var value = opt();
|
|
||||||
if (value is JsApiError apiErr)
|
|
||||||
return Clone(apiErr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
foreach (var opt in opts)
|
||||||
|
value = opt();
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsApiError Clone(JsApiError source) => new()
|
private static JsApiError Clone(JsApiError source) => new()
|
||||||
@@ -406,13 +401,43 @@ public static class JsApiErrors
|
|||||||
Description = source.Description,
|
Description = source.Description,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static JsApiError NewWithTags(JsApiError source, params (string key, string value)[] replacements)
|
private static string[] ToReplacerArgs(params object?[] replacements)
|
||||||
|
{
|
||||||
|
var args = new List<string>(replacements.Length);
|
||||||
|
string key = string.Empty;
|
||||||
|
|
||||||
|
for (var i = 0; i < replacements.Length; i++)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0)
|
||||||
|
{
|
||||||
|
key = replacements[i] as string
|
||||||
|
?? throw new InvalidOperationException("Replacement keys must be strings.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = replacements[i] switch
|
||||||
|
{
|
||||||
|
string s => s,
|
||||||
|
Exception ex => ex.Message,
|
||||||
|
null => string.Empty,
|
||||||
|
var other => other.ToString() ?? string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
args.Add(key);
|
||||||
|
args.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsApiError NewWithTags(JsApiError source, params object?[] replacements)
|
||||||
{
|
{
|
||||||
var clone = Clone(source);
|
var clone = Clone(source);
|
||||||
var description = clone.Description ?? string.Empty;
|
var description = clone.Description ?? string.Empty;
|
||||||
|
var args = ToReplacerArgs(replacements);
|
||||||
|
|
||||||
foreach (var (key, value) in replacements)
|
for (var i = 0; i + 1 < args.Length; i += 2)
|
||||||
description = description.Replace(key, value, StringComparison.Ordinal);
|
description = description.Replace(args[i], args[i + 1], StringComparison.Ordinal);
|
||||||
|
|
||||||
clone.Description = description;
|
clone.Description = description;
|
||||||
return clone;
|
return clone;
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2020-2026 The NATS Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
using Shouldly;
|
||||||
|
|
||||||
|
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
|
||||||
|
|
||||||
|
public sealed class JetStreamErrorsGeneratedConstructorsTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ConstructorSurface_Group01()
|
||||||
|
{
|
||||||
|
JsApiErrors.NewJSAccountResourcesExceededError().ErrCode.ShouldBe(JsApiErrors.AccountResourcesExceeded.ErrCode);
|
||||||
|
JsApiErrors.NewJSAtomicPublishContainsDuplicateMessageError().ErrCode.ShouldBe(JsApiErrors.AtomicPublishContainsDuplicateMessage.ErrCode);
|
||||||
|
JsApiErrors.NewJSAtomicPublishDisabledError().ErrCode.ShouldBe(JsApiErrors.AtomicPublishDisabled.ErrCode);
|
||||||
|
JsApiErrors.NewJSAtomicPublishIncompleteBatchError().ErrCode.ShouldBe(JsApiErrors.AtomicPublishIncompleteBatch.ErrCode);
|
||||||
|
JsApiErrors.NewJSAtomicPublishInvalidBatchCommitError().ErrCode.ShouldBe(JsApiErrors.AtomicPublishInvalidBatchCommit.ErrCode);
|
||||||
|
JsApiErrors.NewJSAtomicPublishInvalidBatchIDError().ErrCode.ShouldBe(JsApiErrors.AtomicPublishInvalidBatchID.ErrCode);
|
||||||
|
JsApiErrors.NewJSAtomicPublishMissingSeqError().ErrCode.ShouldBe(JsApiErrors.AtomicPublishMissingSeq.ErrCode);
|
||||||
|
JsApiErrors.NewJSBadRequestError().ErrCode.ShouldBe(JsApiErrors.BadRequest.ErrCode);
|
||||||
|
JsApiErrors.NewJSClusterIncompleteError().ErrCode.ShouldBe(JsApiErrors.ClusterIncomplete.ErrCode);
|
||||||
|
JsApiErrors.NewJSClusterNotActiveError().ErrCode.ShouldBe(JsApiErrors.ClusterNotActive.ErrCode);
|
||||||
|
JsApiErrors.NewJSClusterNotAssignedError().ErrCode.ShouldBe(JsApiErrors.ClusterNotAssigned.ErrCode);
|
||||||
|
JsApiErrors.NewJSClusterNotAvailError().ErrCode.ShouldBe(JsApiErrors.ClusterNotAvail.ErrCode);
|
||||||
|
JsApiErrors.NewJSClusterNotLeaderError().ErrCode.ShouldBe(JsApiErrors.ClusterNotLeader.ErrCode);
|
||||||
|
JsApiErrors.NewJSClusterPeerNotMemberError().ErrCode.ShouldBe(JsApiErrors.ClusterPeerNotMember.ErrCode);
|
||||||
|
|
||||||
|
JsApiErrors.NewJSAtomicPublishTooLargeBatchError(512).Description.ShouldBe("atomic publish batch is too large: 512");
|
||||||
|
JsApiErrors.NewJSAtomicPublishUnsupportedHeaderBatchError("Nats-Msg-Id").Description.ShouldBe("atomic publish unsupported header used: Nats-Msg-Id");
|
||||||
|
JsApiErrors.NewJSClusterNoPeersError(new InvalidOperationException("no peers")).Description.ShouldBe("no peers");
|
||||||
|
|
||||||
|
var expected = new JsApiError { Code = 499, ErrCode = 9090, Description = "override" };
|
||||||
|
var fromOverride = JsApiErrors.NewJSAccountResourcesExceededError(JsApiErrors.Unless(expected));
|
||||||
|
fromOverride.Code.ShouldBe(expected.Code);
|
||||||
|
fromOverride.ErrCode.ShouldBe(expected.ErrCode);
|
||||||
|
fromOverride.Description.ShouldBe(expected.Description);
|
||||||
|
ReferenceEquals(fromOverride, expected).ShouldBeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2020-2026 The NATS Authors
|
// Copyright 2020-2026 The NATS Authors
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
|
||||||
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
|
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
|
||||||
@@ -97,4 +98,39 @@ public sealed class JetStreamErrorsTests
|
|||||||
JsApiErrors.NewJSPeerRemapError(JsApiErrors.Unless(new Exception("other error"))),
|
JsApiErrors.NewJSPeerRemapError(JsApiErrors.Unless(new Exception("other error"))),
|
||||||
peerRemap).ShouldBeTrue();
|
peerRemap).ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseOpts_WithUnlessApiErrorOption_ReturnsOverride()
|
||||||
|
{
|
||||||
|
var parseOpts = typeof(JsApiErrors).GetMethod(
|
||||||
|
"ParseOpts",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
|
|
||||||
|
parseOpts.ShouldNotBeNull();
|
||||||
|
|
||||||
|
var expected = new JsApiError { Code = 401, ErrCode = 2048, Description = "override" };
|
||||||
|
var result = parseOpts.Invoke(
|
||||||
|
null,
|
||||||
|
[new JsApiErrors.ErrorOption[] { JsApiErrors.Unless(expected) }]);
|
||||||
|
|
||||||
|
result.ShouldBeAssignableTo<JsApiError>()
|
||||||
|
.ErrCode.ShouldBe(expected.ErrCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToReplacerArgs_WithStringErrorAndObject_ConvertsToStringValues()
|
||||||
|
{
|
||||||
|
var toReplacerArgs = typeof(JsApiErrors).GetMethod(
|
||||||
|
"ToReplacerArgs",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
|
|
||||||
|
toReplacerArgs.ShouldNotBeNull();
|
||||||
|
|
||||||
|
var result = toReplacerArgs.Invoke(
|
||||||
|
null,
|
||||||
|
[new object?[] { "{string}", "value", "{error}", new InvalidOperationException("boom"), "{number}", 42 }]);
|
||||||
|
|
||||||
|
result.ShouldBeAssignableTo<string[]>()
|
||||||
|
.ShouldBe(["{string}", "value", "{error}", "boom", "{number}", "42"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
porting.db
BIN
porting.db
Binary file not shown.
77
tools/generate-jetstream-errors.sh
Executable file
77
tools/generate-jetstream-errors.sh
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
output_file="$repo_root/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/JetStreamErrors.GeneratedConstructors.cs"
|
||||||
|
|
||||||
|
simple_methods=(
|
||||||
|
"NewJSAccountResourcesExceededError|AccountResourcesExceeded"
|
||||||
|
"NewJSAtomicPublishContainsDuplicateMessageError|AtomicPublishContainsDuplicateMessage"
|
||||||
|
"NewJSAtomicPublishDisabledError|AtomicPublishDisabled"
|
||||||
|
"NewJSAtomicPublishIncompleteBatchError|AtomicPublishIncompleteBatch"
|
||||||
|
"NewJSAtomicPublishInvalidBatchCommitError|AtomicPublishInvalidBatchCommit"
|
||||||
|
"NewJSAtomicPublishInvalidBatchIDError|AtomicPublishInvalidBatchID"
|
||||||
|
"NewJSAtomicPublishMissingSeqError|AtomicPublishMissingSeq"
|
||||||
|
"NewJSBadRequestError|BadRequest"
|
||||||
|
"NewJSClusterIncompleteError|ClusterIncomplete"
|
||||||
|
"NewJSClusterNotActiveError|ClusterNotActive"
|
||||||
|
"NewJSClusterNotAssignedError|ClusterNotAssigned"
|
||||||
|
"NewJSClusterNotAvailError|ClusterNotAvail"
|
||||||
|
"NewJSClusterNotLeaderError|ClusterNotLeader"
|
||||||
|
"NewJSClusterPeerNotMemberError|ClusterPeerNotMember"
|
||||||
|
)
|
||||||
|
|
||||||
|
templated_methods=(
|
||||||
|
"NewJSAtomicPublishTooLargeBatchError|object?|size|AtomicPublishTooLargeBatch|{size}"
|
||||||
|
"NewJSAtomicPublishUnsupportedHeaderBatchError|object?|header|AtomicPublishUnsupportedHeaderBatch|{header}"
|
||||||
|
"NewJSClusterNoPeersError|Exception|err|ClusterNoPeers|{err}"
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
cat <<'EOF'
|
||||||
|
// Copyright 2020-2026 The NATS Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
//
|
||||||
|
// Generated constructor surface for JetStream API errors.
|
||||||
|
// Source parity: server/jetstream_errors_generated.go
|
||||||
|
|
||||||
|
namespace ZB.MOM.NatsNet.Server;
|
||||||
|
|
||||||
|
public static partial class JsApiErrors
|
||||||
|
{
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for entry in "${simple_methods[@]}"; do
|
||||||
|
IFS='|' read -r method field <<<"$entry"
|
||||||
|
cat <<EOF
|
||||||
|
public static JsApiError ${method}(params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return Clone(${field});
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
done
|
||||||
|
|
||||||
|
for entry in "${templated_methods[@]}"; do
|
||||||
|
IFS='|' read -r method arg_type arg_name field placeholder <<<"$entry"
|
||||||
|
cat <<EOF
|
||||||
|
public static JsApiError ${method}(${arg_type} ${arg_name}, params ErrorOption[] opts)
|
||||||
|
{
|
||||||
|
if (ParseOpts(opts) is JsApiError overridden)
|
||||||
|
return Clone(overridden);
|
||||||
|
|
||||||
|
return NewWithTags(${field}, "${placeholder}", ${arg_name});
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
done
|
||||||
|
|
||||||
|
cat <<'EOF'
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
} >"$output_file"
|
||||||
|
|
||||||
|
echo "Generated $output_file"
|
||||||
Reference in New Issue
Block a user