// Copyright (C) 2023-present sam/u1f320 (vulpine.solutions)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
using Foxnouns.Backend.Database;
using Foxnouns.Backend.Database.Models;
namespace Foxnouns.Backend.Utils;
public static partial class ValidationUtils
{
public static readonly string[] DefaultStatusOptions =
[
"favourite",
"okay",
"jokingly",
"friends_only",
"avoid",
];
public static IEnumerable<(string, ValidationError?)> ValidateFields(
List? fields,
IReadOnlyDictionary customPreferences
)
{
if (fields == null)
return [];
var errors = new List<(string, ValidationError?)>();
if (fields.Count > 25)
{
errors.Add(
(
"fields",
ValidationError.LengthError(
"Too many fields",
0,
Limits.FieldLimit,
fields.Count
)
)
);
}
// No overwhelming this function, thank you
if (fields.Count > 100)
return errors;
foreach ((Field? field, int index) in fields.Select((field, index) => (field, index)))
{
switch (field.Name.Length)
{
case > Limits.FieldNameLimit:
errors.Add(
(
$"fields.{index}.name",
ValidationError.LengthError(
"Field name is too long",
1,
Limits.FieldNameLimit,
field.Name.Length
)
)
);
break;
case < 1:
errors.Add(
(
$"fields.{index}.name",
ValidationError.LengthError(
"Field name is too short",
1,
Limits.FieldNameLimit,
field.Name.Length
)
)
);
break;
}
errors = errors
.Concat(
ValidateFieldEntries(
field.Entries,
customPreferences,
$"fields.{index}.entries"
)
)
.ToList();
}
return errors;
}
public static IEnumerable<(string, ValidationError?)> ValidateFieldEntries(
FieldEntry[]? entries,
IReadOnlyDictionary customPreferences,
string errorPrefix = "fields"
)
{
if (entries == null || entries.Length == 0)
return [];
var errors = new List<(string, ValidationError?)>();
if (entries.Length > Limits.FieldEntriesLimit)
{
errors.Add(
(
errorPrefix,
ValidationError.LengthError(
"Field has too many entries",
0,
Limits.FieldEntriesLimit,
entries.Length
)
)
);
}
// Same as above, no overwhelming this function with a ridiculous amount of entries
if (entries.Length > Limits.FieldEntriesLimit + 50)
return errors;
string[] customPreferenceIds = customPreferences.Keys.Select(id => id.ToString()).ToArray();
foreach (
(FieldEntry? entry, int entryIdx) in entries.Select(
(entry, entryIdx) => (entry, entryIdx)
)
)
{
switch (entry.Value.Length)
{
case > Limits.FieldEntryTextLimit:
errors.Add(
(
$"{errorPrefix}.{entryIdx}.value",
ValidationError.LengthError(
"Field value is too long",
1,
Limits.FieldEntryTextLimit,
entry.Value.Length
)
)
);
break;
case < 1:
errors.Add(
(
$"{errorPrefix}.{entryIdx}.value",
ValidationError.LengthError(
"Field value is too short",
1,
Limits.FieldEntryTextLimit,
entry.Value.Length
)
)
);
break;
}
if (
!DefaultStatusOptions.Contains(entry.Status)
&& !customPreferenceIds.Contains(entry.Status)
)
{
errors.Add(
(
$"{errorPrefix}.{entryIdx}.status",
ValidationError.GenericValidationError("Invalid status", entry.Status)
)
);
}
}
return errors;
}
public static IEnumerable<(string, ValidationError?)> ValidatePronouns(
Pronoun[]? entries,
IReadOnlyDictionary customPreferences,
string errorPrefix = "pronouns"
)
{
if (entries == null || entries.Length == 0)
return [];
var errors = new List<(string, ValidationError?)>();
if (entries.Length > Limits.FieldEntriesLimit)
{
errors.Add(
(
errorPrefix,
ValidationError.LengthError(
"Too many pronouns",
0,
Limits.FieldEntriesLimit,
entries.Length
)
)
);
}
// Same as above, no overwhelming this function with a ridiculous amount of entries
if (entries.Length > Limits.FieldEntriesLimit + 50)
return errors;
string[] customPreferenceIds = customPreferences.Keys.Select(id => id.ToString()).ToArray();
foreach (
(Pronoun? entry, int entryIdx) in entries.Select((entry, entryIdx) => (entry, entryIdx))
)
{
switch (entry.Value.Length)
{
case > Limits.FieldEntryTextLimit:
errors.Add(
(
$"{errorPrefix}.{entryIdx}.value",
ValidationError.LengthError(
"Pronoun value is too long",
1,
Limits.FieldEntryTextLimit,
entry.Value.Length
)
)
);
break;
case < 1:
errors.Add(
(
$"{errorPrefix}.{entryIdx}.value",
ValidationError.LengthError(
"Pronoun value is too short",
1,
Limits.FieldEntryTextLimit,
entry.Value.Length
)
)
);
break;
}
if (entry.DisplayText != null)
{
switch (entry.DisplayText.Length)
{
case > Limits.FieldEntryTextLimit:
errors.Add(
(
$"{errorPrefix}.{entryIdx}.display_text",
ValidationError.LengthError(
"Pronoun display text is too long",
1,
Limits.FieldEntryTextLimit,
entry.Value.Length
)
)
);
break;
case < 1:
errors.Add(
(
$"{errorPrefix}.{entryIdx}.display_text",
ValidationError.LengthError(
"Pronoun display text is too short",
1,
Limits.FieldEntryTextLimit,
entry.Value.Length
)
)
);
break;
}
}
if (
!DefaultStatusOptions.Contains(entry.Status)
&& !customPreferenceIds.Contains(entry.Status)
)
{
errors.Add(
(
$"{errorPrefix}.{entryIdx}.status",
ValidationError.GenericValidationError("Invalid status", entry.Status)
)
);
}
}
return errors;
}
}