using OpenNest;
namespace OpenNest.Data;
///
/// Bounds shared by the set release client and server. Whether the set revisions, nests
/// and nest revisions exist, and whether a created nest already belongs to a release, is
/// checked by the server.
///
public static class SetReleaseRules
{
/// Most kits of one set in one release.
public const int MaxQuantity = 1000;
/// Most sets in one release.
public const int MaxSetLines = 50;
/// One nest per material group.
public const int MaxNests = DrawingSetRules.MaxMembers;
/// Most drawing quantities recorded against one nest: every member of every set.
public const int MaxItemsPerNest = DrawingSetRules.MaxMembers * MaxSetLines;
/// The first rule breaks, or null when it may be posted.
public static string? GetError(SetReleaseRecord? release)
{
if (release is null)
return "Missing release.";
var error = DrawingLibraryRules.TextError("releasedBy", release.ReleasedBy, DrawingLibraryRules.MaxTextLength)
?? DrawingLibraryRules.TextError("notes", release.Notes, DrawingLibraryRules.MaxNotesLength)
?? SetsError(release.Sets);
if (error is not null)
return error;
if (release.Nests is null || release.Nests.Count == 0)
return "nests must list at least one nest.";
if (release.Nests.Count > MaxNests)
return $"A release may have at most {MaxNests} nests.";
var sets = release.Sets.Select(s => s.SetId).ToHashSet();
var seen = new HashSet();
for (var i = 0; i < release.Nests.Count; i++)
{
var nest = release.Nests[i];
if (nest is null)
return $"nests[{i}] is required.";
if (nest.NestId == Guid.Empty)
return $"nests[{i}].nestId is required.";
if (!seen.Add(nest.NestId))
return $"nests[{i}] repeats nest {nest.NestId}.";
error = RevisionsError(i, nest)
?? DrawingLibraryRules.TextError($"nests[{i}].materialName", nest.MaterialName, DrawingLibraryRules.MaxTextLength)
?? DrawingLibraryRules.TextError($"nests[{i}].materialGrade", nest.MaterialGrade, DrawingLibraryRules.MaxTextLength)
?? (!double.IsFinite(nest.Thickness) || nest.Thickness < 0
? $"nests[{i}].thickness must be a finite number, zero or greater."
: null)
?? (!Enum.IsDefined(nest.Units) ? $"nests[{i}].units must be a supported unit name." : null)
?? (!Enum.IsDefined(nest.CutType) ? $"nests[{i}].cutType must be Laser, Waterjet or Plasma." : null)
?? ItemsError(i, nest.Items, sets);
if (error is not null)
return error;
}
return null;
}
/// The first rule breaks, or null when it may be sent.
public static string? GetUndoError(SetReleaseUndoRequest? request)
{
if (request is null)
return "Missing undo request.";
if (request.SkipNests is null)
return "skipNests must be a list.";
if (request.SkipNests.Count > MaxNests)
return $"skipNests may list at most {MaxNests} nests.";
if (request.SkipNests.Contains(Guid.Empty))
return "skipNests may not contain an empty id.";
return DrawingLibraryRules.TextError("undoneBy", request.UndoneBy, DrawingLibraryRules.MaxTextLength);
}
private static string? SetsError(IReadOnlyList? lines)
{
if (lines is null || lines.Count == 0)
return "sets must list at least one set.";
if (lines.Count > MaxSetLines)
return $"A release may have at most {MaxSetLines} sets.";
var seen = new HashSet();
for (var i = 0; i < lines.Count; i++)
{
var line = lines[i];
if (line is null)
return $"sets[{i}] is required.";
if (line.SetId == Guid.Empty)
return $"sets[{i}].setId is required.";
if (!seen.Add(line.SetId))
return $"sets[{i}] repeats set {line.SetId}.";
if (line.SetRevision < 1)
return $"sets[{i}].setRevision must be a positive revision.";
if (line.Quantity < 1 || line.Quantity > MaxQuantity)
return $"sets[{i}].quantity must be between 1 and {MaxQuantity}.";
}
return null;
}
private static string? RevisionsError(int i, SetReleaseNest nest)
{
if (!Enum.IsDefined(nest.Kind))
return $"nests[{i}].kind must be created or merged.";
if (nest.RevisionAfter < 1)
return $"nests[{i}].revisionAfter must be a positive revision.";
if (nest.Kind == SetReleaseNestKind.Created)
return nest.RevisionBefore is null ? null : $"nests[{i}].revisionBefore must be empty for a created nest.";
if (nest.RevisionBefore is not { } before || before < 1)
return $"nests[{i}].revisionBefore is required for a merged nest.";
return nest.RevisionAfter > before ? null : $"nests[{i}].revisionAfter must be later than revisionBefore.";
}
private static string? ItemsError(int i, IReadOnlyList? items, HashSet sets)
{
if (items is null)
return $"nests[{i}].items is required.";
if (items.Count > MaxItemsPerNest)
return $"nests[{i}] may record at most {MaxItemsPerNest} items.";
var seen = new HashSet<(Guid, Guid)>();
for (var j = 0; j < items.Count; j++)
{
var item = items[j];
var name = $"nests[{i}].items[{j}]";
if (item is null)
return $"{name} is required.";
if (!sets.Contains(item.SetId))
return $"{name}.setId must be one of the release's sets.";
if (item.DrawingId == Guid.Empty)
return $"{name}.drawingId is required.";
if (!seen.Add((item.SetId, item.DrawingId)))
return $"{name} repeats drawing {item.DrawingId} of set {item.SetId}.";
if (item.DrawingRevision < 1)
return $"{name}.drawingRevision must be a positive revision.";
if (item.Quantity < 1)
return $"{name}.quantity must be at least 1.";
}
return null;
}
}