Files
MBN_STOCK_WEBVIEW/src/MBN_STOCK_WEBVIEW.LegacyApplication/LegacyOperatorSettings.cs

980 lines
32 KiB
C#

using System.Text.Json;
namespace MBN_STOCK_WEBVIEW.LegacyApplication;
public sealed record LegacyOperatorSettings(
string? SceneDirectory,
string? ResourceDirectory,
string? BackgroundDirectory,
bool NavigationExpanded)
{
public const int CurrentSchemaVersion = 1;
public static LegacyOperatorSettings Default { get; } = new(
SceneDirectory: null,
ResourceDirectory: null,
BackgroundDirectory: null,
NavigationExpanded: true);
}
public enum LegacyOperatorSettingsFolderKind
{
Scene,
Resource,
Background
}
public enum LegacyOperatorFolderValidationFailure
{
None,
InvalidPath,
DirectoryNotFound,
RootDirectory,
NonFixedDrive,
ReparsePoint,
RequiredFileMissing,
RequiredFileUnsafe,
InvalidRuntimeCatalog,
FileUnavailable
}
public sealed record LegacyOperatorValidatedFolder(
string CanonicalPath,
string AbbreviatedPath,
bool DatabaseIniDetected);
public sealed record LegacyOperatorFolderValidationResult(
LegacyOperatorValidatedFolder? Folder,
LegacyOperatorFolderValidationFailure Failure,
string? WarningMessage)
{
public bool IsValid => Failure == LegacyOperatorFolderValidationFailure.None &&
Folder is not null;
}
/// <summary>
/// Validates only native folder-picker results. The returned messages intentionally
/// contain no source path; a separately bounded abbreviation is available for display.
/// </summary>
public static class LegacyOperatorFolderValidator
{
public const int MaximumPathCharacters = 1024;
public const int DefaultDisplayCharacters = 48;
public const string UnconfiguredDisplay = "선택되지 않음";
private const string InvalidFolderMessage =
"선택한 폴더를 안전하게 사용할 수 없습니다.";
private const string MissingSceneMessage =
"선택한 디자인 폴더에 필수 장면 파일이 없습니다.";
private const string InvalidResourceMessage =
"선택한 설정 폴더의 UI 설정 파일을 검증할 수 없습니다.";
private static readonly string[] ResourceFileNames =
[
"종목.ini",
"업종_코스피.ini",
"업종_코스닥.ini",
"해외.ini",
"환율.ini",
"지수.ini",
"종목비교.ini"
];
public static bool TryValidate(
LegacyOperatorSettingsFolderKind kind,
string? path,
out LegacyOperatorValidatedFolder? folder,
out string? warningMessage)
{
var result = Validate(kind, path);
folder = result.Folder;
warningMessage = result.WarningMessage;
return result.IsValid;
}
public static LegacyOperatorFolderValidationResult Validate(
LegacyOperatorSettingsFolderKind kind,
string? path)
{
var failure = LegacyOperatorFolderValidationFailure.InvalidPath;
if (!Enum.IsDefined(kind) || !TryCanonicalizeDirectory(
path,
out var canonicalPath,
out failure))
{
return Invalid(failure, InvalidFolderMessage);
}
var databaseIniDetected = false;
switch (kind)
{
case LegacyOperatorSettingsFolderKind.Scene:
if (!TryValidateRegularFile(
Path.Combine(canonicalPath, "5001.t2s"),
required: true,
out failure))
{
return Invalid(failure, MissingSceneMessage);
}
break;
case LegacyOperatorSettingsFolderKind.Resource:
foreach (var fileName in ResourceFileNames)
{
if (!TryValidateRegularFile(
Path.Combine(canonicalPath, fileName),
required: true,
out failure))
{
return Invalid(failure, InvalidResourceMessage);
}
}
var stockMenu = LegacyRuntimeStockCutMenuLoader
.ResolveFromResourceDirectory(canonicalPath);
if (stockMenu.UsesBuiltInCatalog ||
stockMenu.Failure != LegacyRuntimeStockCutMenuFailure.None)
{
return Invalid(
LegacyOperatorFolderValidationFailure.InvalidRuntimeCatalog,
InvalidResourceMessage);
}
var uiCatalogs = LegacyRuntimeUiCatalogLoader
.ResolveFromResourceDirectory(canonicalPath);
if (uiCatalogs.Warnings.Count != 0)
{
return Invalid(
LegacyOperatorFolderValidationFailure.InvalidRuntimeCatalog,
InvalidResourceMessage);
}
var databaseIniPath = Path.Combine(canonicalPath, "MmoneyCoder.ini");
if (!TryInspectOptionalRegularFile(
databaseIniPath,
out databaseIniDetected,
out failure))
{
return Invalid(failure, InvalidResourceMessage);
}
break;
case LegacyOperatorSettingsFolderKind.Background:
break;
default:
return Invalid(
LegacyOperatorFolderValidationFailure.InvalidPath,
InvalidFolderMessage);
}
return new LegacyOperatorFolderValidationResult(
new LegacyOperatorValidatedFolder(
canonicalPath,
AbbreviateForDisplay(canonicalPath),
databaseIniDetected),
LegacyOperatorFolderValidationFailure.None,
WarningMessage: null);
}
public static string AbbreviateForDisplay(
string? path,
int maximumCharacters = DefaultDisplayCharacters)
{
maximumCharacters = Math.Clamp(maximumCharacters, 12, 120);
if (string.IsNullOrWhiteSpace(path) ||
path.Any(char.IsControl) ||
!Path.IsPathFullyQualified(path))
{
return UnconfiguredDisplay;
}
try
{
var canonicalPath = Path.TrimEndingDirectorySeparator(Path.GetFullPath(path));
var root = Path.GetPathRoot(canonicalPath);
var leaf = Path.GetFileName(canonicalPath);
if (string.IsNullOrEmpty(root) || string.IsNullOrEmpty(leaf))
{
return UnconfiguredDisplay;
}
var display = string.Concat(
Path.TrimEndingDirectorySeparator(root),
Path.DirectorySeparatorChar,
"…",
Path.DirectorySeparatorChar,
leaf);
if (display.Length <= maximumCharacters)
{
return display;
}
var prefix = string.Concat(
Path.TrimEndingDirectorySeparator(root),
Path.DirectorySeparatorChar,
"…",
Path.DirectorySeparatorChar);
var availableLeafCharacters = Math.Max(
1,
maximumCharacters - prefix.Length - 1);
return string.Concat(
prefix,
"…",
leaf[^Math.Min(availableLeafCharacters, leaf.Length)..]);
}
catch (Exception exception) when (
exception is ArgumentException or IOException or NotSupportedException or
System.Security.SecurityException)
{
return UnconfiguredDisplay;
}
}
internal static bool TryCanonicalizeDirectory(
string? path,
out string canonicalPath,
out LegacyOperatorFolderValidationFailure failure)
{
canonicalPath = string.Empty;
if (string.IsNullOrWhiteSpace(path) ||
path.Length > MaximumPathCharacters ||
path.Any(char.IsControl) ||
!Path.IsPathFullyQualified(path))
{
failure = LegacyOperatorFolderValidationFailure.InvalidPath;
return false;
}
try
{
canonicalPath = Path.TrimEndingDirectorySeparator(Path.GetFullPath(path));
var root = Path.GetPathRoot(canonicalPath);
if (string.IsNullOrEmpty(root) ||
root.StartsWith("\\\\", StringComparison.Ordinal) ||
!string.Equals(
Path.GetPathRoot(root),
root,
StringComparison.OrdinalIgnoreCase))
{
failure = LegacyOperatorFolderValidationFailure.InvalidPath;
canonicalPath = string.Empty;
return false;
}
if (string.Equals(
Path.TrimEndingDirectorySeparator(root),
canonicalPath,
StringComparison.OrdinalIgnoreCase))
{
failure = LegacyOperatorFolderValidationFailure.RootDirectory;
canonicalPath = string.Empty;
return false;
}
var drive = new DriveInfo(root);
if (!drive.IsReady || drive.DriveType != DriveType.Fixed)
{
failure = LegacyOperatorFolderValidationFailure.NonFixedDrive;
canonicalPath = string.Empty;
return false;
}
if (!Directory.Exists(canonicalPath))
{
failure = LegacyOperatorFolderValidationFailure.DirectoryNotFound;
canonicalPath = string.Empty;
return false;
}
for (DirectoryInfo? current = new(canonicalPath);
current is not null;
current = current.Parent)
{
if ((current.Attributes & FileAttributes.ReparsePoint) != 0)
{
failure = LegacyOperatorFolderValidationFailure.ReparsePoint;
canonicalPath = string.Empty;
return false;
}
}
failure = LegacyOperatorFolderValidationFailure.None;
return true;
}
catch (Exception exception) when (
exception is ArgumentException or IOException or UnauthorizedAccessException or
NotSupportedException or System.Security.SecurityException)
{
failure = LegacyOperatorFolderValidationFailure.FileUnavailable;
canonicalPath = string.Empty;
return false;
}
}
private static bool TryValidateRegularFile(
string path,
bool required,
out LegacyOperatorFolderValidationFailure failure)
{
try
{
if (!File.Exists(path))
{
failure = required
? LegacyOperatorFolderValidationFailure.RequiredFileMissing
: LegacyOperatorFolderValidationFailure.None;
return !required;
}
var attributes = File.GetAttributes(path);
if ((attributes & (FileAttributes.Directory |
FileAttributes.ReparsePoint |
FileAttributes.Device)) != 0)
{
failure = LegacyOperatorFolderValidationFailure.RequiredFileUnsafe;
return false;
}
failure = LegacyOperatorFolderValidationFailure.None;
return true;
}
catch (Exception exception) when (
exception is ArgumentException or IOException or UnauthorizedAccessException or
NotSupportedException or System.Security.SecurityException)
{
failure = LegacyOperatorFolderValidationFailure.FileUnavailable;
return false;
}
}
private static bool TryInspectOptionalRegularFile(
string path,
out bool detected,
out LegacyOperatorFolderValidationFailure failure)
{
detected = false;
try
{
var attributes = File.GetAttributes(path);
if ((attributes & (FileAttributes.Directory |
FileAttributes.ReparsePoint |
FileAttributes.Device)) != 0)
{
failure = LegacyOperatorFolderValidationFailure.RequiredFileUnsafe;
return false;
}
detected = true;
failure = LegacyOperatorFolderValidationFailure.None;
return true;
}
catch (Exception exception) when (
exception is FileNotFoundException or DirectoryNotFoundException)
{
failure = LegacyOperatorFolderValidationFailure.None;
return true;
}
catch (Exception exception) when (
exception is ArgumentException or IOException or UnauthorizedAccessException or
NotSupportedException or System.Security.SecurityException)
{
failure = LegacyOperatorFolderValidationFailure.FileUnavailable;
return false;
}
}
private static LegacyOperatorFolderValidationResult Invalid(
LegacyOperatorFolderValidationFailure failure,
string warningMessage) => new(
Folder: null,
failure,
warningMessage);
}
public enum LegacyOperatorSettingsStoreFailure
{
None,
InvalidConfigurationPath,
FileUnavailable,
FileTooLarge,
InvalidJson,
UnsupportedSchema,
InvalidSettings,
SaveUnavailable
}
public sealed record LegacyOperatorSettingsLoadResult(
LegacyOperatorSettings Settings,
string? WarningMessage)
{
public LegacyOperatorSettingsStoreFailure Failure { get; init; }
public bool LoadedFromDisk { get; init; }
}
public sealed record LegacyOperatorSettingsSaveResult(
bool Succeeded,
string? WarningMessage)
{
public LegacyOperatorSettingsStoreFailure Failure { get; init; }
public LegacyOperatorSettings? SavedSettings { get; init; }
}
/// <summary>
/// Stores only native-approved runtime folders and one presentation preference.
/// Credentials, playout targets, live authorization and asset contents never enter
/// this file.
/// </summary>
public sealed class LegacyOperatorSettingsStore
{
public const int MaximumFileBytes = 16 * 1024;
public const string LoadWarningMessage =
"저장된 폴더 설정을 읽지 못해 안전한 기본값을 사용합니다.";
public const string SaveWarningMessage =
"폴더 설정을 안전하게 저장하지 못했습니다.";
private const string FileName = "runtime-folders.local.json";
private readonly string _configurationPath;
public LegacyOperatorSettingsStore(string? configurationPath = null)
{
_configurationPath = configurationPath ?? DefaultPath;
}
public static string DefaultPath => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MBN_STOCK_WEBVIEW",
"Config",
FileName);
public string ConfigurationPath => _configurationPath;
public LegacyOperatorSettingsLoadResult Load()
{
if (!TryNormalizeConfigurationPath(_configurationPath, out var path))
{
return FailedLoad(LegacyOperatorSettingsStoreFailure.InvalidConfigurationPath);
}
try
{
var directory = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(directory))
{
return FailedLoad(
LegacyOperatorSettingsStoreFailure.InvalidConfigurationPath);
}
if (!Directory.Exists(directory))
{
return new LegacyOperatorSettingsLoadResult(
LegacyOperatorSettings.Default,
WarningMessage: null)
{
Failure = LegacyOperatorSettingsStoreFailure.None,
LoadedFromDisk = false
};
}
if (!LegacyOperatorFolderValidator.TryCanonicalizeDirectory(
directory,
out var canonicalDirectory,
out _))
{
return FailedLoad(
LegacyOperatorSettingsStoreFailure.InvalidConfigurationPath);
}
path = Path.Combine(canonicalDirectory, Path.GetFileName(path));
if (!File.Exists(path))
{
return new LegacyOperatorSettingsLoadResult(
LegacyOperatorSettings.Default,
WarningMessage: null)
{
Failure = LegacyOperatorSettingsStoreFailure.None,
LoadedFromDisk = false
};
}
var attributes = File.GetAttributes(path);
if ((attributes & (FileAttributes.Directory |
FileAttributes.ReparsePoint |
FileAttributes.Device)) != 0)
{
return FailedLoad(LegacyOperatorSettingsStoreFailure.FileUnavailable);
}
using var stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
bufferSize: 4096,
FileOptions.SequentialScan);
if (stream.Length is <= 0 or > MaximumFileBytes)
{
return FailedLoad(
stream.Length > MaximumFileBytes
? LegacyOperatorSettingsStoreFailure.FileTooLarge
: LegacyOperatorSettingsStoreFailure.InvalidJson);
}
var json = new byte[(int)stream.Length];
stream.ReadExactly(json);
if (!TryDeserialize(
json,
out var settings,
out var failure) ||
!TryNormalizeSettings(settings, out var normalizedSettings))
{
return FailedLoad(
failure == LegacyOperatorSettingsStoreFailure.None
? LegacyOperatorSettingsStoreFailure.InvalidSettings
: failure);
}
return new LegacyOperatorSettingsLoadResult(
normalizedSettings,
WarningMessage: null)
{
Failure = LegacyOperatorSettingsStoreFailure.None,
LoadedFromDisk = true
};
}
catch (Exception exception) when (
exception is ArgumentException or IOException or UnauthorizedAccessException or
NotSupportedException or System.Security.SecurityException)
{
return FailedLoad(LegacyOperatorSettingsStoreFailure.FileUnavailable);
}
}
public LegacyOperatorSettingsSaveResult Save(LegacyOperatorSettings settings)
{
ArgumentNullException.ThrowIfNull(settings);
if (!TryNormalizeSettings(settings, out var normalizedSettings))
{
return FailedSave(LegacyOperatorSettingsStoreFailure.InvalidSettings);
}
if (!TryNormalizeConfigurationPath(_configurationPath, out var path))
{
return FailedSave(
LegacyOperatorSettingsStoreFailure.InvalidConfigurationPath);
}
string? temporaryPath = null;
try
{
var directory = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(directory))
{
return FailedSave(
LegacyOperatorSettingsStoreFailure.InvalidConfigurationPath);
}
if (!TryCreateSafeConfigurationDirectory(
directory,
out var canonicalDirectory))
{
return FailedSave(
LegacyOperatorSettingsStoreFailure.InvalidConfigurationPath);
}
path = Path.Combine(canonicalDirectory, Path.GetFileName(path));
if (File.Exists(path))
{
var attributes = File.GetAttributes(path);
if ((attributes & (FileAttributes.Directory |
FileAttributes.ReparsePoint |
FileAttributes.Device)) != 0)
{
return FailedSave(
LegacyOperatorSettingsStoreFailure.SaveUnavailable);
}
}
var json = Serialize(normalizedSettings);
if (json.Length > MaximumFileBytes)
{
return FailedSave(LegacyOperatorSettingsStoreFailure.FileTooLarge);
}
temporaryPath = Path.Combine(
canonicalDirectory,
$".{FileName}.{Guid.NewGuid():N}.tmp");
using (var stream = new FileStream(
temporaryPath,
FileMode.CreateNew,
FileAccess.Write,
FileShare.None,
bufferSize: 4096,
FileOptions.WriteThrough))
{
stream.Write(json);
stream.Flush(flushToDisk: true);
}
File.Move(temporaryPath, path, overwrite: true);
temporaryPath = null;
return new LegacyOperatorSettingsSaveResult(
Succeeded: true,
WarningMessage: null)
{
Failure = LegacyOperatorSettingsStoreFailure.None,
SavedSettings = normalizedSettings
};
}
catch (Exception exception) when (
exception is ArgumentException or IOException or UnauthorizedAccessException or
NotSupportedException or System.Security.SecurityException)
{
return FailedSave(LegacyOperatorSettingsStoreFailure.SaveUnavailable);
}
finally
{
if (temporaryPath is not null)
{
try
{
File.Delete(temporaryPath);
}
catch (Exception exception) when (
exception is IOException or UnauthorizedAccessException or
NotSupportedException or System.Security.SecurityException)
{
// A failed save remains failed. Never replace that result with a
// cleanup exception or expose the temporary path.
}
}
}
}
private static bool TryNormalizeSettings(
LegacyOperatorSettings settings,
out LegacyOperatorSettings normalizedSettings)
{
normalizedSettings = LegacyOperatorSettings.Default;
if (!TryNormalizeOptionalFolder(
LegacyOperatorSettingsFolderKind.Scene,
settings.SceneDirectory,
out var sceneDirectory) ||
!TryNormalizeOptionalFolder(
LegacyOperatorSettingsFolderKind.Resource,
settings.ResourceDirectory,
out var resourceDirectory) ||
!TryNormalizeOptionalFolder(
LegacyOperatorSettingsFolderKind.Background,
settings.BackgroundDirectory,
out var backgroundDirectory))
{
return false;
}
normalizedSettings = new LegacyOperatorSettings(
sceneDirectory,
resourceDirectory,
backgroundDirectory,
settings.NavigationExpanded);
return true;
}
private static bool TryNormalizeOptionalFolder(
LegacyOperatorSettingsFolderKind kind,
string? path,
out string? canonicalPath)
{
canonicalPath = null;
if (path is null)
{
return true;
}
if (!LegacyOperatorFolderValidator.TryValidate(
kind,
path,
out var folder,
out _))
{
return false;
}
canonicalPath = folder!.CanonicalPath;
return true;
}
private static byte[] Serialize(LegacyOperatorSettings settings)
{
using var stream = new MemoryStream(capacity: 512);
using (var writer = new Utf8JsonWriter(
stream,
new JsonWriterOptions { Indented = true }))
{
writer.WriteStartObject();
writer.WriteNumber("schemaVersion", LegacyOperatorSettings.CurrentSchemaVersion);
WriteOptionalString(writer, "sceneDirectory", settings.SceneDirectory);
WriteOptionalString(writer, "resourceDirectory", settings.ResourceDirectory);
WriteOptionalString(writer, "backgroundDirectory", settings.BackgroundDirectory);
writer.WriteBoolean("navigationExpanded", settings.NavigationExpanded);
writer.WriteEndObject();
}
return stream.ToArray();
}
private static void WriteOptionalString(
Utf8JsonWriter writer,
string name,
string? value)
{
if (value is null)
{
writer.WriteNull(name);
}
else
{
writer.WriteString(name, value);
}
}
private static bool TryDeserialize(
ReadOnlyMemory<byte> json,
out LegacyOperatorSettings settings,
out LegacyOperatorSettingsStoreFailure failure)
{
settings = LegacyOperatorSettings.Default;
failure = LegacyOperatorSettingsStoreFailure.InvalidJson;
try
{
using var document = JsonDocument.Parse(
json,
new JsonDocumentOptions
{
AllowTrailingCommas = false,
CommentHandling = JsonCommentHandling.Disallow,
MaxDepth = 4
});
if (document.RootElement.ValueKind != JsonValueKind.Object)
{
return false;
}
var properties = new Dictionary<string, JsonElement>(StringComparer.Ordinal);
foreach (var property in document.RootElement.EnumerateObject())
{
if (!IsExpectedProperty(property.Name) ||
!properties.TryAdd(property.Name, property.Value))
{
return false;
}
}
if (properties.Count != 5 ||
!TryReadInt32(properties, "schemaVersion", out var schemaVersion) ||
!TryReadOptionalString(properties, "sceneDirectory", out var sceneDirectory) ||
!TryReadOptionalString(
properties,
"resourceDirectory",
out var resourceDirectory) ||
!TryReadOptionalString(
properties,
"backgroundDirectory",
out var backgroundDirectory) ||
!TryReadBoolean(
properties,
"navigationExpanded",
out var navigationExpanded))
{
return false;
}
if (schemaVersion != LegacyOperatorSettings.CurrentSchemaVersion)
{
failure = LegacyOperatorSettingsStoreFailure.UnsupportedSchema;
return false;
}
settings = new LegacyOperatorSettings(
sceneDirectory,
resourceDirectory,
backgroundDirectory,
navigationExpanded);
failure = LegacyOperatorSettingsStoreFailure.None;
return true;
}
catch (JsonException)
{
return false;
}
catch (InvalidOperationException)
{
return false;
}
catch (FormatException)
{
return false;
}
catch (OverflowException)
{
return false;
}
}
private static bool IsExpectedProperty(string name) => name is
"schemaVersion" or
"sceneDirectory" or
"resourceDirectory" or
"backgroundDirectory" or
"navigationExpanded";
private static bool TryReadInt32(
IReadOnlyDictionary<string, JsonElement> properties,
string name,
out int value)
{
value = default;
return properties.TryGetValue(name, out var element) &&
element.ValueKind == JsonValueKind.Number &&
element.TryGetInt32(out value);
}
private static bool TryReadBoolean(
IReadOnlyDictionary<string, JsonElement> properties,
string name,
out bool value)
{
value = default;
if (!properties.TryGetValue(name, out var element) ||
element.ValueKind is not (JsonValueKind.True or JsonValueKind.False))
{
return false;
}
value = element.GetBoolean();
return true;
}
private static bool TryReadOptionalString(
IReadOnlyDictionary<string, JsonElement> properties,
string name,
out string? value)
{
value = null;
if (!properties.TryGetValue(name, out var element))
{
return false;
}
if (element.ValueKind == JsonValueKind.Null)
{
return true;
}
if (element.ValueKind != JsonValueKind.String)
{
return false;
}
value = element.GetString();
return value is not null;
}
private static bool TryNormalizeConfigurationPath(
string? path,
out string normalizedPath)
{
normalizedPath = string.Empty;
if (string.IsNullOrWhiteSpace(path) ||
path.Length > LegacyOperatorFolderValidator.MaximumPathCharacters ||
path.Any(char.IsControl) ||
!Path.IsPathFullyQualified(path))
{
return false;
}
try
{
normalizedPath = Path.GetFullPath(path);
var root = Path.GetPathRoot(normalizedPath);
var fileName = Path.GetFileName(normalizedPath);
return !string.IsNullOrEmpty(root) &&
!root.StartsWith("\\\\", StringComparison.Ordinal) &&
!string.IsNullOrWhiteSpace(fileName) &&
!fileName.Any(char.IsControl);
}
catch (Exception exception) when (
exception is ArgumentException or IOException or NotSupportedException or
System.Security.SecurityException)
{
normalizedPath = string.Empty;
return false;
}
}
private static bool TryCreateSafeConfigurationDirectory(
string directory,
out string canonicalDirectory)
{
canonicalDirectory = string.Empty;
try
{
var existingAncestor = directory;
while (!Directory.Exists(existingAncestor))
{
var parent = Path.GetDirectoryName(existingAncestor);
if (string.IsNullOrEmpty(parent) ||
string.Equals(parent, existingAncestor, StringComparison.OrdinalIgnoreCase))
{
return false;
}
existingAncestor = parent;
}
if (!LegacyOperatorFolderValidator.TryCanonicalizeDirectory(
existingAncestor,
out _,
out _))
{
return false;
}
Directory.CreateDirectory(directory);
return LegacyOperatorFolderValidator.TryCanonicalizeDirectory(
directory,
out canonicalDirectory,
out _);
}
catch (Exception exception) when (
exception is ArgumentException or IOException or UnauthorizedAccessException or
NotSupportedException or System.Security.SecurityException)
{
canonicalDirectory = string.Empty;
return false;
}
}
private static LegacyOperatorSettingsLoadResult FailedLoad(
LegacyOperatorSettingsStoreFailure failure) => new(
LegacyOperatorSettings.Default,
LoadWarningMessage)
{
Failure = failure,
LoadedFromDisk = false
};
private static LegacyOperatorSettingsSaveResult FailedSave(
LegacyOperatorSettingsStoreFailure failure) => new(
Succeeded: false,
SaveWarningMessage)
{
Failure = failure,
SavedSettings = null
};
}