using System.IO;
using System.Text;
namespace zero.Persistence;
public class Safenames
{
public enum Scope
{
Url,
File
}
const char HYPHEN = '-';
const char DOT = '.';
const char PLUS = '+';
const char AMPERSAND = '&';
///
/// Converts an untrusted to a safe filename
///
public static string File(string value)
{
return Generate(Path.GetFileName(value), Scope.File);
}
///
/// Converts a term to a safe alias (suitable for URLs)
///
public static string Alias(string value)
{
return Generate(value, Scope.Url);
}
///
/// Converts a term to a safe alias (suitable for URLs)
///
public static string Alias(object value)
{
return Generate(value?.ToString(), Scope.Url);
}
///
///
///
static string Generate(string value, Scope scope)
{
if (String.IsNullOrWhiteSpace(value))
{
return String.Empty;
}
char previous = default;
StringBuilder output = new();
for (int i = 0; i < value.Length; i++)
{
// get character in lower case
char character = char.ToLower(value[i]);
char target;
// do not handle surrogates
if (char.IsSurrogate(character))
{
continue;
}
// special replacements accents + umlauts
if (character.TryReplaceAccent(out char[] replacement))
{
if (replacement.Length > 1)
{
output.Append(replacement);
output.Remove(output.Length - 1, 1);
}
target = replacement[replacement.Length - 1];
}
// append character a-z, 0-9
else if (character.IsAZor09())
{
target = character;
}
// + sign for + and &
else if (character == PLUS || character == AMPERSAND)
{
target = PLUS;
}
else if (scope == Scope.File && character == DOT)
{
target = DOT;
}
// add hyphen for all other characters
else
{
target = HYPHEN;
}
// add default characters
if (target != HYPHEN && target != PLUS)
{
output.Append(target);
}
// add hyphen if it isn't first and previous char is not + or -
else if (target == HYPHEN && previous != default && previous != PLUS && previous != HYPHEN)
{
output.Append(target);
}
// add plus. do remove hyphen it is the previous character
else if (target == PLUS)
{
if (previous == HYPHEN)
{
output.Remove(output.Length - 1, 1);
}
output.Append(target);
}
if (output.Length > 0)
{
previous = output[output.Length - 1];
}
}
if (output.Length > 0 && !output[output.Length - 1].IsAZor09())
{
output.Remove(output.Length - 1, 1);
}
if (output.Length == 0)
{
output.Append(HYPHEN);
}
return output.ToString();
}
}