using OpenNest; namespace OpenNest.Data; /// /// Bounds shared by the drawing library client and server. The client checks them /// before sending; the server refuses anything outside them with no state change. /// public static class DrawingLibraryRules { /// Longest name, customer, material, grade or saved-by text. public const int MaxTextLength = 200; public const int MaxNotesLength = 4000; /// Largest accepted drawing package (16 MiB). public const int MaxPackageBytes = 16 * 1024 * 1024; public const string PackageTooLargeError = "The drawing package exceeds 16 MiB."; public const int DefaultRevisionLimit = 100; public const int MaxRevisionLimit = 200; /// Thickness as stored and compared: rounded to four decimals. public static double NormalizeThickness(double thickness) => System.Math.Round(thickness, 4, MidpointRounding.AwayFromZero); /// The first metadata rule breaks, or null when it may be saved. public static string? GetMetadataError(DrawingLibraryRecord? record) { if (record is null) return "Missing metadata."; if (string.IsNullOrWhiteSpace(record.Name)) return "name is required."; return TextError("name", record.Name, MaxTextLength) ?? TextError("customer", record.Customer, MaxTextLength) ?? TextError("materialName", record.MaterialName, MaxTextLength) ?? TextError("materialGrade", record.MaterialGrade, MaxTextLength) ?? TextError("savedBy", record.SavedBy, MaxTextLength) ?? TextError("notes", record.Notes, MaxNotesLength) ?? (!double.IsFinite(record.Thickness) || record.Thickness < 0 ? "thickness must be a finite number, zero or greater." : null) ?? (!Enum.IsDefined(record.Units) ? "units must be a supported unit name." : null) ?? (!Enum.IsDefined(record.CutType) ? "cutType must be Laser, Waterjet or Plasma." : null) ?? (record.Settings is null ? "settings is required." : record.Settings.GetValidationError()); } /// The first package rule breaks, or null when it may be stored. public static string? GetPackageError(ReadOnlySpan package) { if (package.Length == 0) return "The drawing package is empty."; if (package.Length > MaxPackageBytes) return PackageTooLargeError; // Every ZIP archive, including an empty one, starts with a "PK" record signature. if (package.Length < 4 || package[0] != (byte)'P' || package[1] != (byte)'K') return "The drawing package is not a ZIP archive."; return null; } internal static string? TextError(string field, string? value, int maxLength) { if (value is null) return $"{field} is required."; if (value.Length > maxLength) return $"{field} must be at most {maxLength} characters."; // SQLite LIKE stops at an embedded NUL; refuse it everywhere for one rule. if (value.Contains('\0')) return $"{field} must not contain NUL characters."; return null; } }