using System.Net.Http;
using System.Text.Json;
namespace OpenNest.Data;
///
/// Duplicate-name checks for a nest's first upload and for renames. Names may already be
/// on posted CNC programs and labels, so a nest is never renamed silently: callers offer
/// a replacement, or warn, only when another active nest has the exact same name.
///
public static class NestNameConflicts
{
///
/// A freshly allocated name for when an active server nest
/// already uses ; null when the name is free, or when the
/// check or the allocation fails, so the caller keeps the name it has.
///
public static async Task FindReplacementAsync(
INestRepository repository, string name, int year, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(repository);
if (string.IsNullOrWhiteSpace(name))
return null;
try
{
if (!await IsNameTakenAsync(repository, name, cancellationToken: cancellationToken))
return null;
var number = await repository.AllocateNestNumberAsync(year, cancellationToken);
return new NestNameAllocation { Year = year, Number = number, Name = NestNames.Format(year, number) };
}
catch (Exception ex) when (IsCheckFailure(ex))
{
return null;
}
}
///
/// , or null when the server could not answer
/// (offline, too slow, or an older server), so the caller can go ahead without warning.
///
public static async Task TryIsNameTakenAsync(
INestRepository repository, string name, Guid excludeId = default,
CancellationToken cancellationToken = default)
{
try
{
return await IsNameTakenAsync(repository, name, excludeId, cancellationToken);
}
catch (Exception ex) when (IsCheckFailure(ex))
{
return null;
}
}
///
/// True when an active (not deleted) server nest other than
/// is named exactly .
///
public static async Task IsNameTakenAsync(
INestRepository repository, string name, Guid excludeId = default,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(repository);
// The search is a case-insensitive substring match; page through it for the exact name.
for (var offset = 0; ;)
{
var page = await repository.QueryAsync(new NestQuery
{
Search = name,
Sort = NestSortField.Name,
Descending = false,
Offset = offset,
Limit = NestQuery.MaxLimit,
}, cancellationToken);
if (page.Items.Any(r => !r.Deleted && r.Id != excludeId
&& string.Equals(r.Name, name, StringComparison.Ordinal)))
return true;
offset += page.Items.Count;
if (page.Items.Count == 0 || offset >= page.Total)
return false;
}
}
private static bool IsCheckFailure(Exception ex) =>
ex is HttpRequestException or IOException or OperationCanceledException
or NotSupportedException or ArgumentException or JsonException or InvalidOperationException;
}