using System;
using System.Collections.Generic;
using AIImages.Settings;
namespace AIImages.Validation
{
///
/// Валидатор для настроек мода AI Images
///
public static class SettingsValidator
{
///
/// Валидирует все настройки и возвращает список ошибок
///
public static ValidationResult Validate(AIImagesModSettings settings)
{
var result = new ValidationResult();
if (settings == null)
{
result.AddError("Settings object is null");
return result;
}
// Валидация API endpoint
ValidateApiEndpoint(settings.apiEndpoint, result);
// Валидация размеров изображения
ValidateImageDimensions(settings.width, settings.height, result);
// Валидация steps
ValidateSteps(settings.steps, result);
// Валидация CFG scale
ValidateCfgScale(settings.cfgScale, result);
// Валидация sampler и scheduler
ValidateSamplerAndScheduler(
settings.selectedSampler,
settings.selectedScheduler,
result
);
// Валидация пути сохранения
ValidateSavePath(settings.savePath, result);
return result;
}
private static void ValidateApiEndpoint(string endpoint, ValidationResult result)
{
if (string.IsNullOrWhiteSpace(endpoint))
{
result.AddError("API endpoint cannot be empty");
return;
}
if (!Uri.TryCreate(endpoint, UriKind.Absolute, out Uri uri))
{
result.AddError($"Invalid API endpoint format: {endpoint}");
return;
}
if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
{
result.AddWarning($"API endpoint should use HTTP or HTTPS protocol: {endpoint}");
}
// Проверка на localhost/127.0.0.1
if (
uri.Host != "localhost"
&& uri.Host != "127.0.0.1"
&& !uri.Host.StartsWith("192.168.")
)
{
result.AddWarning(
"API endpoint is not pointing to a local address. Make sure the API is accessible."
);
}
}
private static void ValidateImageDimensions(int width, int height, ValidationResult result)
{
const int minDimension = 64;
const int maxDimension = 2048;
const int recommendedMin = 512;
const int recommendedMax = 1024;
if (width < minDimension || width > maxDimension)
{
result.AddError(
$"Width must be between {minDimension} and {maxDimension}. Current: {width}"
);
}
if (height < minDimension || height > maxDimension)
{
result.AddError(
$"Height must be between {minDimension} and {maxDimension}. Current: {height}"
);
}
// Проверка кратности 8 (рекомендация для Stable Diffusion)
if (width % 8 != 0)
{
result.AddWarning(
$"Width should be divisible by 8 for optimal results. Current: {width}"
);
}
if (height % 8 != 0)
{
result.AddWarning(
$"Height should be divisible by 8 for optimal results. Current: {height}"
);
}
// Предупреждения о производительности
if (width > recommendedMax || height > recommendedMax)
{
result.AddWarning(
$"Large image dimensions ({width}x{height}) may result in slow generation and high memory usage"
);
}
if (width < recommendedMin || height < recommendedMin)
{
result.AddWarning(
$"Small image dimensions ({width}x{height}) may result in lower quality"
);
}
}
private static void ValidateSteps(int steps, ValidationResult result)
{
const int minSteps = 1;
const int maxSteps = 150;
const int recommendedMin = 20;
const int recommendedMax = 50;
if (steps < minSteps || steps > maxSteps)
{
result.AddError(
$"Steps must be between {minSteps} and {maxSteps}. Current: {steps}"
);
}
if (steps < recommendedMin)
{
result.AddWarning(
$"Low steps value ({steps}) may result in lower quality. Recommended: {recommendedMin}-{recommendedMax}"
);
}
if (steps > recommendedMax)
{
result.AddWarning(
$"High steps value ({steps}) may result in slow generation with minimal quality improvement"
);
}
}
private static void ValidateCfgScale(float cfgScale, ValidationResult result)
{
const float minCfg = 1.0f;
const float maxCfg = 30.0f;
const float recommendedMin = 5.0f;
const float recommendedMax = 15.0f;
if (cfgScale < minCfg || cfgScale > maxCfg)
{
result.AddError(
$"CFG Scale must be between {minCfg} and {maxCfg}. Current: {cfgScale}"
);
}
if (cfgScale < recommendedMin)
{
result.AddWarning(
$"Low CFG scale ({cfgScale}) may ignore prompt. Recommended: {recommendedMin}-{recommendedMax}"
);
}
if (cfgScale > recommendedMax)
{
result.AddWarning(
$"High CFG scale ({cfgScale}) may result in over-saturated or distorted images"
);
}
}
private static void ValidateSamplerAndScheduler(
string sampler,
string scheduler,
ValidationResult result
)
{
if (string.IsNullOrWhiteSpace(sampler))
{
result.AddWarning("Sampler is not selected. A default sampler will be used.");
}
if (string.IsNullOrWhiteSpace(scheduler))
{
result.AddWarning("Scheduler is not selected. A default scheduler will be used.");
}
}
private static void ValidateSavePath(string savePath, ValidationResult result)
{
if (string.IsNullOrWhiteSpace(savePath))
{
result.AddError("Save path cannot be empty");
return;
}
// Проверка на недопустимые символы в пути
char[] invalidChars = System.IO.Path.GetInvalidPathChars();
if (savePath.IndexOfAny(invalidChars) >= 0)
{
result.AddError($"Save path contains invalid characters: {savePath}");
}
}
}
///
/// Результат валидации настроек
///
public class ValidationResult
{
private readonly List _errors = new List();
private readonly List _warnings = new List();
public bool IsValid => _errors.Count == 0;
public bool HasWarnings => _warnings.Count > 0;
public IReadOnlyList Errors => _errors;
public IReadOnlyList Warnings => _warnings;
public void AddError(string error)
{
if (!string.IsNullOrWhiteSpace(error))
{
_errors.Add(error);
}
}
public void AddWarning(string warning)
{
if (!string.IsNullOrWhiteSpace(warning))
{
_warnings.Add(warning);
}
}
public string GetErrorsAsString()
{
return string.Join("\n", _errors);
}
public string GetWarningsAsString()
{
return string.Join("\n", _warnings);
}
public string GetAllMessagesAsString()
{
var messages = new List();
if (_errors.Count > 0)
{
messages.Add("Errors:");
messages.AddRange(_errors);
}
if (_warnings.Count > 0)
{
if (messages.Count > 0)
messages.Add("");
messages.Add("Warnings:");
messages.AddRange(_warnings);
}
return string.Join("\n", messages);
}
}
}