| | | 1 | | namespace AsyncResponse.Transports.NATS; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Resolves NATS subject and JetStream stream names from transport options. NATS stream names cannot |
| | | 5 | | /// contain dots, so a stream defaulted from the subject prefix replaces dots with underscores. These |
| | | 6 | | /// names are deployment contracts: changing them while messages are in flight strands unprocessed |
| | | 7 | | /// worker or response messages on the old stream. |
| | | 8 | | /// </summary> |
| | 3 | 9 | | internal sealed class NatsTransportSubjectSchema(NatsAsyncResponseTransportOptions _options) |
| | | 10 | | { |
| | 3 | 11 | | public string WorkerSubject => ResolveSubject(_options.WorkerSubject, "worker"); |
| | 3 | 12 | | public string ResponseSubject => ResolveSubject(_options.ResponseSubject, "response"); |
| | 3 | 13 | | public string DeadLetterSubject => ResolveSubject(_options.DeadLetterSubject, "deadletter"); |
| | | 14 | | |
| | 3 | 15 | | public string WorkerStream => ResolveStream(_options.WorkerStream, WorkerSubject); |
| | 3 | 16 | | public string ResponseStream => ResolveStream(_options.ResponseStream, ResponseSubject); |
| | 3 | 17 | | public string DeadLetterStream => ResolveStream(_options.DeadLetterStream, DeadLetterSubject); |
| | | 18 | | |
| | | 19 | | private string ResolveSubject(string? configured, string role) |
| | 3 | 20 | | => !string.IsNullOrWhiteSpace(configured) |
| | 3 | 21 | | ? configured |
| | 3 | 22 | | : $"{_options.SubjectPrefix}.transport.{role}"; |
| | | 23 | | |
| | | 24 | | private static string ResolveStream(string? configured, string subject) |
| | 3 | 25 | | => !string.IsNullOrWhiteSpace(configured) |
| | 3 | 26 | | ? configured |
| | 3 | 27 | | : SanitizeStreamName(subject); |
| | | 28 | | |
| | | 29 | | /// <summary>Turns a subject into a valid JetStream stream name (NATS stream names forbid dots and wildcards).</summ |
| | | 30 | | internal static string SanitizeStreamName(string subject) |
| | | 31 | | { |
| | 3 | 32 | | Span<char> buffer = stackalloc char[subject.Length]; |
| | 3 | 33 | | for (var i = 0; i < subject.Length; i++) |
| | | 34 | | { |
| | 3 | 35 | | var c = subject[i]; |
| | 3 | 36 | | buffer[i] = char.IsAsciiLetterOrDigit(c) || c is '-' or '_' ? c : '_'; |
| | | 37 | | } |
| | | 38 | | |
| | 3 | 39 | | return new string(buffer); |
| | | 40 | | } |
| | | 41 | | } |