src/Elastic.Markdown/Myst/FrontMatter/Applicability.cs (158 lines of code) (raw):
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Elastic.Markdown.Helpers;
using YamlDotNet.Serialization;
namespace Elastic.Markdown.Myst.FrontMatter;
[YamlSerializable]
public record AppliesCollection : IReadOnlyCollection<Applicability>
{
private readonly Applicability[] _items;
public AppliesCollection(Applicability[] items) => _items = items;
// <lifecycle> [version]
public static bool TryParse(string? value, out AppliesCollection? availability)
{
availability = null;
if (string.IsNullOrWhiteSpace(value) || string.Equals(value.Trim(), "all", StringComparison.InvariantCultureIgnoreCase))
{
availability = GenerallyAvailable;
return true;
}
var items = value.Split(',');
var applications = new List<Applicability>(items.Length);
foreach (var item in items)
{
if (Applicability.TryParse(item.Trim(), out var a))
applications.Add(a);
}
if (applications.Count == 0)
return false;
availability = new AppliesCollection([.. applications]);
return true;
}
public virtual bool Equals(AppliesCollection? other)
{
if ((object)this == other)
return true;
if ((object?)other is null || EqualityContract != other.EqualityContract)
return false;
var comparer = StructuralComparisons.StructuralEqualityComparer;
return comparer.Equals(_items, other._items);
}
public override int GetHashCode()
{
var comparer = StructuralComparisons.StructuralEqualityComparer;
return
(EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295)
+ comparer.GetHashCode(_items);
}
public static explicit operator AppliesCollection(string b)
{
var productAvailability = TryParse(b, out var version) ? version : null;
return productAvailability ?? throw new ArgumentException($"'{b}' is not a valid applicability string array.");
}
public static AppliesCollection GenerallyAvailable { get; }
= new([Applicability.GenerallyAvailable]);
public override string ToString()
{
if (this == GenerallyAvailable)
return "all";
var sb = new StringBuilder();
foreach (var item in _items)
_ = sb.Append(item).Append(", ");
return sb.ToString();
}
public IEnumerator<Applicability> GetEnumerator() => ((IEnumerable<Applicability>)_items).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Count => _items.Length;
}
[YamlSerializable]
public record Applicability
{
public ProductLifecycle Lifecycle { get; init; }
public SemVersion? Version { get; init; }
public static Applicability GenerallyAvailable { get; } = new()
{
Lifecycle = ProductLifecycle.GenerallyAvailable,
Version = AllVersions.Instance
};
public string GetLifeCycleName() =>
Lifecycle switch
{
ProductLifecycle.TechnicalPreview => "Technical Preview",
ProductLifecycle.Beta => "Beta",
ProductLifecycle.Development => "Development",
ProductLifecycle.Deprecated => "Deprecated",
ProductLifecycle.Planned => "Planned",
ProductLifecycle.Discontinued => "Discontinued",
ProductLifecycle.Unavailable => "Unavailable",
ProductLifecycle.GenerallyAvailable => "GA",
_ => throw new ArgumentOutOfRangeException(nameof(Lifecycle), Lifecycle, null)
};
public override string ToString()
{
if (this == GenerallyAvailable)
return "all";
var sb = new StringBuilder();
var lifecycle = Lifecycle switch
{
ProductLifecycle.TechnicalPreview => "preview",
ProductLifecycle.Beta => "beta",
ProductLifecycle.Development => "dev",
ProductLifecycle.Deprecated => "deprecated",
ProductLifecycle.Planned => "planned",
ProductLifecycle.Discontinued => "discontinued",
ProductLifecycle.Unavailable => "unavailable",
ProductLifecycle.GenerallyAvailable => "ga",
_ => throw new ArgumentOutOfRangeException()
};
_ = sb.Append(lifecycle);
if (Version is not null && Version != AllVersions.Instance)
_ = sb.Append(' ').Append(Version);
return sb.ToString();
}
public static explicit operator Applicability(string b)
{
var productAvailability = TryParse(b, out var version) ? version : TryParse(b + ".0", out version) ? version : null;
return productAvailability ?? throw new ArgumentException($"'{b}' is not a valid applicability string.");
}
public static bool TryParse(string? value, [NotNullWhen(true)] out Applicability? availability)
{
if (string.IsNullOrWhiteSpace(value) || string.Equals(value.Trim(), "all", StringComparison.InvariantCultureIgnoreCase))
{
availability = GenerallyAvailable;
return true;
}
var tokens = value.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length < 1)
{
availability = null;
return false;
}
var lifecycle = tokens[0].ToLowerInvariant() switch
{
"preview" => ProductLifecycle.TechnicalPreview,
"tech-preview" => ProductLifecycle.TechnicalPreview,
"beta" => ProductLifecycle.Beta,
"dev" => ProductLifecycle.Development,
"development" => ProductLifecycle.Development,
"deprecated" => ProductLifecycle.Deprecated,
"coming" => ProductLifecycle.Planned,
"planned" => ProductLifecycle.Planned,
"discontinued" => ProductLifecycle.Discontinued,
"unavailable" => ProductLifecycle.Unavailable,
"ga" => ProductLifecycle.GenerallyAvailable,
_ => throw new Exception($"Unknown product lifecycle: {tokens[0]}")
};
var version = tokens.Length < 2
? null
: tokens[1] switch
{
null => AllVersions.Instance,
"all" => AllVersions.Instance,
"" => AllVersions.Instance,
var t => SemVersionConverter.TryParse(t, out var v) ? v : null
};
availability = new Applicability { Version = version, Lifecycle = lifecycle };
return true;
}
}