blob: ba732cec9330b08e034a54be2e9f79851f936ac6 [file] [log] [blame]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace DotPulsar;
using DotPulsar.Internal.Extensions;
using DotPulsar.Internal.PulsarApi;
/// <summary>
/// Unique identifier of a single message.
/// </summary>
public sealed class MessageId : IEquatable<MessageId>, IComparable<MessageId>
{
static MessageId()
{
Earliest = new MessageId(ulong.MaxValue, ulong.MaxValue, -1, -1);
Latest = new MessageId(long.MaxValue, long.MaxValue, -1, -1);
}
/// <summary>
/// The oldest message available in the topic.
/// </summary>
public static MessageId Earliest { get; }
/// <summary>
/// The next message published in the topic.
/// </summary>
public static MessageId Latest { get; }
/// <summary>
/// Initializes a new instance using the specified ledgerId, entryId, partition, and batchIndex.
/// </summary>
public MessageId(ulong ledgerId, ulong entryId, int partition, int batchIndex, string topic = "")
{
LedgerId = ledgerId;
EntryId = entryId;
Partition = partition;
BatchIndex = batchIndex;
Topic = topic;
}
/// <summary>
/// The id of the ledger.
/// </summary>
public ulong LedgerId { get; }
/// <summary>
/// The id of the entry.
/// </summary>
public ulong EntryId { get; }
/// <summary>
/// The partition.
/// </summary>
public int Partition { get; }
/// <summary>
/// The batch index.
/// </summary>
public int BatchIndex { get; }
/// <summary>
/// Return the full topic name of a message.
/// </summary>
public string Topic { get; }
public int CompareTo(MessageId? other)
{
if (other is null)
return 1;
var result = LedgerId.CompareTo(other.LedgerId);
if (result != 0)
return result;
result = EntryId.CompareTo(other.EntryId);
if (result != 0)
return result;
result = Partition.CompareTo(other.Partition);
if (result != 0)
return result;
result = String.Compare(Topic, other.Topic, StringComparison.Ordinal);
if (result != 0)
return result;
return BatchIndex.CompareTo(other.BatchIndex);
}
public static bool operator >(MessageId x, MessageId y)
=> x is not null && x.CompareTo(y) >= 1;
public static bool operator <(MessageId x, MessageId y)
=> x is not null ? x.CompareTo(y) <= -1 : y is not null;
public static bool operator >=(MessageId x, MessageId y)
=> x is not null ? x.CompareTo(y) >= 0 : y is null;
public static bool operator <=(MessageId x, MessageId y)
=> x is null || x.CompareTo(y) <= 0;
public override bool Equals(object? o)
=> o is MessageId id && Equals(id);
public bool Equals(MessageId? other)
=> other is not null && LedgerId == other.LedgerId && EntryId == other.EntryId && Partition == other.Partition && BatchIndex == other.BatchIndex && Topic == other.Topic;
public static bool operator ==(MessageId x, MessageId y)
=> ReferenceEquals(x, y) || (x is not null && x.Equals(y));
public static bool operator !=(MessageId x, MessageId y)
=> !(x == y);
public override int GetHashCode()
=> HashCode.Combine(LedgerId, EntryId, Partition, BatchIndex, Topic);
public override string ToString()
{
if (Topic == string.Empty)
return $"{LedgerId}:{EntryId}:{Partition}:{BatchIndex}";
return $"{LedgerId}:{EntryId}:{Partition}:{BatchIndex}:{Topic}";
}
private static bool TryParse(ReadOnlySpan<char> s, out int result)
{
#if NETSTANDARD2_0
return Int32.TryParse(s.ToString(), out result);
#else
return Int32.TryParse(s, out result);
#endif
}
private static bool TryParse(ReadOnlySpan<char> s, out ulong result)
{
#if NETSTANDARD2_0
return UInt64.TryParse(s.ToString(), out result);
#else
return UInt64.TryParse(s, out result);
#endif
}
/// <summary>
/// Converts the string representation of a message id to its object equivalent. A return value indicates whether the conversion succeeded.
/// </summary>
/// <param name="s">A string containing a message id to convert.</param>
/// <param name="result">
/// When this method returns, contains the MessageId equivalent of the string contained in s, if the conversion succeeded, or MessageId.Earliest if the conversion failed.
/// The conversion fails if the s parameter is null or Empty, or is not of the correct format.
/// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
/// </param>
/// <returns> true if the string was converted successfully; otherwise, false. </returns>
public static bool TryParse(string s, out MessageId result)
{
result = Earliest;
if (string.IsNullOrWhiteSpace(s))
return false;
var input = s.AsMemory();
var startOfNextEntry = 0;
var index = 0;
var field = 0;
ulong ledgerId = 0;
ulong entryId = 0;
int partition = 0;
int batchIndex = 0;
var topic = string.Empty;
while (index <= s.Length)
{
if (index == s.Length || s[index] == ':')
{
var length = index - startOfNextEntry;
if (length == 0)
return false;
var value = input.Slice(startOfNextEntry, length);
if (field == 0 && !TryParse(value.Span, out ledgerId))
return false;
else if (field == 1 && !TryParse(value.Span, out entryId))
return false;
else if (field == 2 && !TryParse(value.Span, out partition))
return false;
else if (field == 3)
{
if (!TryParse(value.Span, out batchIndex))
return false;
if (index + 1 < s.Length)
topic = s.Substring(index + 1);
break;
}
startOfNextEntry = index + 1;
++field;
}
++index;
}
result = new MessageId(ledgerId, entryId, partition, batchIndex, topic);
return true;
}
internal MessageIdData ToMessageIdData()
{
var messageIdData = new MessageIdData();
messageIdData.MapFrom(this);
return messageIdData;
}
}