using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ICU4N.Text;
namespace Jellyfin.Extensions
{
///
/// Provides extensions methods for .
///
public static partial class StringExtensions
{
private static readonly Lazy _transliteratorId = new(() =>
Environment.GetEnvironmentVariable("JELLYFIN_TRANSLITERATOR_ID")
?? "Any-Latin; Latin-Ascii; Lower; NFD; [:Nonspacing Mark:] Remove; [:Punctuation:] Remove;");
private static readonly Lazy _transliterator = new(() =>
{
try
{
return Transliterator.GetInstance(_transliteratorId.Value);
}
catch (ArgumentException)
{
return null;
}
});
// Matches non-conforming unicode chars
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
[GeneratedRegex("([\ud800-\udbff](?![\udc00-\udfff]))|((?
/// Removes the diacritics character from the strings.
///
/// The string to act on.
/// The string without diacritics character.
public static string RemoveDiacritics(this string text)
=> Diacritics.Extensions.StringExtensions.RemoveDiacritics(
NonConformingUnicodeRegex().Replace(text, string.Empty));
///
/// Checks whether or not the specified string has diacritics in it.
///
/// The string to check.
/// True if the string has diacritics, false otherwise.
public static bool HasDiacritics(this string text)
=> Diacritics.Extensions.StringExtensions.HasDiacritics(text)
|| NonConformingUnicodeRegex().IsMatch(text);
///
/// Counts the number of occurrences of [needle] in the string.
///
/// The haystack to search in.
/// The character to search for.
/// The number of occurrences of the [needle] character.
public static int Count(this ReadOnlySpan value, char needle)
{
var count = 0;
var length = value.Length;
for (var i = 0; i < length; i++)
{
if (value[i] == needle)
{
count++;
}
}
return count;
}
///
/// Returns the part on the left of the needle.
///
/// The string to seek.
/// The needle to find.
/// The part left of the .
public static ReadOnlySpan LeftPart(this ReadOnlySpan haystack, char needle)
{
if (haystack.IsEmpty)
{
return ReadOnlySpan.Empty;
}
var pos = haystack.IndexOf(needle);
return pos == -1 ? haystack : haystack[..pos];
}
///
/// Returns the part on the right of the needle.
///
/// The string to seek.
/// The needle to find.
/// The part right of the .
public static ReadOnlySpan RightPart(this ReadOnlySpan haystack, char needle)
{
if (haystack.IsEmpty)
{
return ReadOnlySpan.Empty;
}
var pos = haystack.LastIndexOf(needle);
if (pos == -1)
{
return haystack;
}
if (pos == haystack.Length - 1)
{
return ReadOnlySpan.Empty;
}
return haystack[(pos + 1)..];
}
///
/// Returns a transliterated string which only contain ascii characters.
///
/// The string to act on.
/// The transliterated string.
public static string Transliterated(this string text)
{
return (_transliterator.Value is null) ? text : _transliterator.Value.Transliterate(text);
}
///
/// Ensures all strings are non-null and trimmed of leading an trailing blanks.
///
/// The enumerable of strings to trim.
/// The enumeration of trimmed strings.
public static IEnumerable Trimmed(this IEnumerable values)
{
return values.Select(i => (i ?? string.Empty).Trim());
}
///
/// Truncates a string at the first null character ('\0').
///
/// The input string.
///
/// The substring up to (but not including) the first null character,
/// or the original string if no null character is present.
///
public static string TruncateAtNull(this string text)
{
return string.IsNullOrEmpty(text) ? text : text.AsSpan().LeftPart('\0').ToString();
}
}
}