using System;
namespace LaunchDarkly.EventSource
{
///
/// Represents the Server-Sent Event message received from a stream.
///
///
///
/// An SSE event consists of an event name (defaulting to "message" if not specified),
/// a data string, and an optional "ID" string that the server may provide.
///
///
/// The event name and ID properties are always stored as strings. By default, the
/// data property is also stored as a string. However, in some applications, it may
/// be desirable to represent the data as a UTF-8 byte array (for instance, if you are
/// using the System.Text.Json API to parse JSON data).
///
///
/// Since strings in .NET use two-byte UTF-16 characters, if you have a large block of
/// UTF-8 data it is considerably more efficient to process it in its original form
/// rather than converting it to or from a string. stores
/// data as strings by default, but you set
/// it can store the raw UTF-8 data instead. In either case, MessageEvent will
/// convert types transparently so that you can read either
/// or .
///
///
public struct MessageEvent
{
///
/// The default value of if the SSE stream did not specify an
/// event: field.
///
public const string DefaultName = "message";
#region Private Fields
private readonly string _name;
private readonly string _dataString;
private readonly Utf8ByteSpan _dataUtf8Bytes;
private readonly string _lastEventId;
private readonly Uri _origin;
#endregion
#region Public Constructors
///
/// Initializes a new instance of the class.
///
/// the event name
/// the data received in the server-sent event
/// the last event identifier, or null
/// the origin URI of the stream
public MessageEvent(string name, string data, string lastEventId, Uri origin)
{
_name = name;
_dataString = data;
_dataUtf8Bytes = new Utf8ByteSpan();
_lastEventId = lastEventId;
_origin = origin;
}
///
/// Initializes a new instance of the class,
/// providing the data as a UTF-8 byte span.
///
/// the event name
/// the data received in the server-sent event;
/// the MessageEvent will store a reference to the byte array, rather than
/// copying it, so it should not be modified afterward by the caller
///
/// the last event identifier, or null
/// the origin URI of the stream
public MessageEvent(string name, Utf8ByteSpan dataUtf8Bytes, string lastEventId, Uri origin)
{
_name = name;
_dataString = null;
_dataUtf8Bytes = dataUtf8Bytes;
_lastEventId = lastEventId;
_origin = origin;
}
///
/// Initializes a new instance of the class.
///
/// the event name
/// the data received in the server-sent event
/// the origin URI of the stream
///
/// The will be initialized to null.
///
public MessageEvent(string name, string data, Uri origin) : this(name, data, null, origin) { }
#endregion
#region Public Properties
///
/// The event name.
///
///
/// This can be specified by the server in the event: field in the SSE data, as in
/// event: my-event-name. If there is no event: field, the default name
/// is .
///
public string Name => _name;
///
/// Gets the data received in the event as a string.
///
///
///
/// This is the value of the data: field in the SSE data; if there are multiple
/// data: lines for a single event, they are concatenated with "\n".
///
///
/// If the data was originally stored as a string, the same string is returned.
/// If it was stored as a UTF-8 byte array, the bytes are copied to a new string.
///
///
public string Data => _dataString is null ? _dataUtf8Bytes.GetString() : _dataString;
///
/// Gets the data received in the event as a UTF-8 byte span.
///
///
///
/// This is the value of the data: field in the SSE data; if there are multiple
/// data: lines for a single event, they are concatenated with "\n".
///
///
/// If the data was originally stored as UTF-8 bytes, the returned value refers to
/// the same array, offset, and length (it is the caller's responsibility not to
/// modify the byte array). If it was originally stored as a string, the string
/// is copied to a new byte array.
///
///
public Utf8ByteSpan DataUtf8Bytes => _dataString is null ? _dataUtf8Bytes :
new Utf8ByteSpan(_dataString);
///
/// Gets the last event identifier received in the server-sent event.
///
///
/// This is the value of the id: field in the SSE data. If there is no such
/// field, it is null. You can use a previously received
/// value with when starting
/// a new to tell the server what the last event you
/// received was, although not all servers support this.
///
public string LastEventId => _lastEventId;
///
/// Gets the origin URI of the stream that generated the server-sent event.
///
public Uri Origin => _origin;
///
/// True if the event data is stored internally as UTF-8 bytes.
///
///
/// The data can be accessed with either or
/// regardless of the value of this property. The property only indicates the original
/// format of the data, so, for instance, if it is then
/// reading will have more overhead (due to copying) than
/// reading .
///
public bool IsDataUtf8Bytes => _dataString is null;
#endregion
#region Public Methods
///
/// Determines whether the specified is equal to this instance.
///
///
/// This method is potentially inefficient and should be used only in testing.
///
/// the to compare with this instance
/// if the instances are equal
public override bool Equals(object obj)
{
if (!(obj is MessageEvent that) ||
!object.Equals(Name, that.Name) ||
!object.Equals(LastEventId, that.LastEventId) ||
!object.Equals(Origin, that.Origin))
{
return false;
}
if (this._dataString != null)
{
return that._dataString != null ?
this._dataString == that._dataString :
that._dataUtf8Bytes.Equals(this._dataString);
}
return that._dataString != null ?
this._dataUtf8Bytes.Equals(that._dataString) :
this._dataUtf8Bytes.Equals(that._dataUtf8Bytes);
}
///
/// Returns a hash code for this instance.
///
///
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// This method is potentially inefficient and should be used only in testing.
///
public override int GetHashCode()
{
int hash = 17;
hash = hash * 31 + (_dataString != null ? _dataString.GetHashCode() : _dataUtf8Bytes.GetString().GetHashCode());
hash = hash * 31 + (_lastEventId != null ? _lastEventId.GetHashCode() : 0);
hash = hash * 31 + (_origin != null ? _origin.GetHashCode() : 0);
return hash;
}
#endregion
}
}