using OpenNest;
namespace OpenNest.Data;
///
/// Bounds shared by the set release client and server. Whether the set revisions, drawings,
/// 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;
/// Most drawings released on their own in one release, as many as one set may list.
public const int MaxDrawingLines = DrawingSetRules.MaxMembers;
/// Most of one drawing released on its own, as for one set member.
public const int MaxDrawingQuantity = DrawingSetRules.MaxQuantity;
/// One nest per material group.
public const int MaxNests = DrawingSetRules.MaxMembers;
/// Most drawing quantities recorded against one nest: every member of every set, and every drawing on its own.
public const int MaxItemsPerNest = DrawingSetRules.MaxMembers * MaxSetLines + MaxDrawingLines;
/// 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, release.Drawings)
?? DrawingsError(release.Drawings);
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 drawings = release.Drawings.Select(d => d.DrawingId).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, drawings);
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);
}
// A release names at least one set or one drawing on its own.
private static string? SetsError(IReadOnlyList? lines, IReadOnlyList? drawings)
{
if (lines is null)
return "sets must be a list.";
if (lines.Count == 0 && (drawings is null || drawings.Count == 0))
return "sets or drawings must list at least one set or drawing.";
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? DrawingsError(IReadOnlyList? drawings)
{
if (drawings is null)
return "drawings must be a list.";
if (drawings.Count > MaxDrawingLines)
return $"A release may have at most {MaxDrawingLines} drawings on their own.";
var seen = new HashSet();
for (var i = 0; i < drawings.Count; i++)
{
var drawing = drawings[i];
if (drawing is null)
return $"drawings[{i}] is required.";
if (drawing.DrawingId == Guid.Empty)
return $"drawings[{i}].drawingId is required.";
if (!seen.Add(drawing.DrawingId))
return $"drawings[{i}] repeats drawing {drawing.DrawingId}.";
if (drawing.Quantity < 1 || drawing.Quantity > MaxDrawingQuantity)
return $"drawings[{i}].quantity must be between 1 and {MaxDrawingQuantity}.";
}
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.";
}
// An item with no set is the quantity of a drawing released on its own.
private static string? ItemsError(int i, IReadOnlyList? items, HashSet sets,
HashSet drawings)
{
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 (item.SetId is { } setId && !sets.Contains(setId))
return $"{name}.setId must be one of the release's sets.";
if (item.DrawingId == Guid.Empty)
return $"{name}.drawingId is required.";
if (item.SetId is null && !drawings.Contains(item.DrawingId))
return $"{name} has no setId, so drawingId must be one of the release's drawings.";
if (!seen.Add((item.SetId, item.DrawingId)))
return item.SetId is null
? $"{name} repeats drawing {item.DrawingId}."
: $"{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;
}
}