2024-11-22 07:40:43 +01:00
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using System.Text.Json.Serialization;
|
|
|
|
|
|
|
|
|
|
namespace Uwaa.HTTP;
|
|
|
|
|
|
2024-11-22 09:09:58 +01:00
|
|
|
|
//TODO: Add support for parameters
|
|
|
|
|
|
2024-11-22 07:40:43 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Represents a Multipurpose Internet Mail Extensions type, which indicates the nature and format of a document/file/chunk of data.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[JsonConverter(typeof(MIMETypeConverter))]
|
|
|
|
|
public readonly record struct MIMEType
|
|
|
|
|
{
|
|
|
|
|
public static explicit operator string(MIMEType type) => type.ToString();
|
|
|
|
|
|
|
|
|
|
public static implicit operator MIMEType(string type) => new MIMEType(type);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The first part of the MIME type, representing the general category into which the data type falls.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public readonly string? Type { get; init; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The second part of the MIME type, identifying the exact kind of data the MIME type represents.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public readonly string? Subtype { get; init; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parses a MIME type string.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="text">The string to parse.</param>
|
|
|
|
|
/// <exception cref="FormatException">Thrown if MIME type is missing a subtype.</exception>
|
|
|
|
|
public MIMEType(ReadOnlySpan<char> text)
|
|
|
|
|
{
|
|
|
|
|
if (text == "*/*")
|
|
|
|
|
{
|
|
|
|
|
Type = null;
|
|
|
|
|
Subtype = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int spl = text.IndexOf('/');
|
|
|
|
|
if (spl == -1)
|
|
|
|
|
{
|
|
|
|
|
//MIME types need a subtype to be valid.
|
|
|
|
|
throw new FormatException("The provided MIME type is missing a subtype.");
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-22 09:09:58 +01:00
|
|
|
|
int end = text.IndexOf(';');
|
|
|
|
|
if (end == -1)
|
|
|
|
|
end = text.Length;
|
|
|
|
|
|
2024-11-22 07:40:43 +01:00
|
|
|
|
if (spl == 1 && text[0] == '*')
|
|
|
|
|
Type = null;
|
|
|
|
|
else
|
|
|
|
|
Type = new string(text[..spl]);
|
|
|
|
|
|
|
|
|
|
if (spl == text.Length - 2 && text[^1] == '*')
|
|
|
|
|
Subtype = null;
|
|
|
|
|
else
|
2024-11-22 09:09:58 +01:00
|
|
|
|
Subtype = new string(text[(spl + 1)..end]);
|
2024-11-22 07:40:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs a MIME type from its two components.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public MIMEType(string? type, string? subType)
|
|
|
|
|
{
|
|
|
|
|
Type = type;
|
|
|
|
|
Subtype = subType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines if the given MIME type matches the pattern specified by this MIME type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public readonly bool Match(MIMEType type)
|
|
|
|
|
{
|
|
|
|
|
return (Type == null || Type.Equals(type.Type, StringComparison.OrdinalIgnoreCase)) && (Subtype == null || Subtype.Equals(type.Subtype, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Generates a string for the MIME type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override readonly string ToString() => $"{Type ?? "*"}/{Subtype ?? "*"}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MIMETypeConverter : JsonConverter<MIMEType>
|
|
|
|
|
{
|
|
|
|
|
public sealed override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(MIMEType);
|
|
|
|
|
|
|
|
|
|
public override MIMEType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
|
|
|
{
|
2024-12-09 23:30:43 +01:00
|
|
|
|
string? str = reader.GetString() ?? throw new JsonException("Cannot read MIME type");
|
2024-11-22 07:40:43 +01:00
|
|
|
|
return new MIMEType(str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Write(Utf8JsonWriter writer, MIMEType status, JsonSerializerOptions options)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteStringValue(status.ToString());
|
|
|
|
|
}
|
|
|
|
|
}
|