using System.Text.Json; using System.Text.Json.Serialization; using AIStudio.Settings; namespace AIStudio.Tools; /// /// Stores a path below the data directory relative to it, and every other path as it is. /// /// /// AI Studio keeps files of its own below the data directory: the transcripts of a chat, the copies of /// the tokenizers, and more. A stored path to such a file outlives the computer it was written on. A /// backup brings it to another computer, where the user name, the OS, or the data directory itself may /// differ. Relative to the data directory, the path still names the same file there. In memory, the /// path stays absolute, so the code which works with it never notices.

/// /// A relative path is written with forward slashes, which every OS reads. A path to a file of the user /// stays absolute: it lies outside of the data directory, and a backup does not bring that file along. /// A path written before AI Studio stored such paths relative is absolute as well, and is read as it /// is. While the data directory is not known yet, every path stays as it is. ///
public sealed class DataDirectoryRelativePathConverter : JsonConverter { private const char STORED_SEPARATOR = '/'; #region Overrides of JsonConverter /// public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => ToAbsolute(reader.GetString() ?? string.Empty, SettingsManager.DataDirectory); /// public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => writer.WriteStringValue(ToStored(value, SettingsManager.DataDirectory)); #endregion /// /// Turns a path into the form in which it is stored. /// /// The absolute path, as AI Studio works with it. /// The data directory, or null while it is not known yet. /// The path relative to the data directory when it lies below it, otherwise the path as it is. internal static string ToStored(string path, string? dataDirectory) { if (string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(dataDirectory) || !Path.IsPathFullyQualified(path) || !PathTools.IsInsideDirectory(dataDirectory, path)) return path; return Path.GetRelativePath(Path.GetFullPath(dataDirectory), Path.GetFullPath(path)).Replace(Path.DirectorySeparatorChar, STORED_SEPARATOR); } /// /// Turns a stored path back into the absolute path AI Studio works with. /// /// /// A relative path which leads out of the data directory was not written by AI Studio. Rather than /// resolving it against whatever directory the process runs in, it is read as no path at all. /// /// The path as it was stored. /// The data directory, or null while it is not known yet. /// The absolute path. internal static string ToAbsolute(string storedPath, string? dataDirectory) { if (string.IsNullOrWhiteSpace(storedPath) || string.IsNullOrWhiteSpace(dataDirectory) || Path.IsPathRooted(storedPath)) return storedPath; var path = Path.Join(dataDirectory, storedPath.Replace(STORED_SEPARATOR, Path.DirectorySeparatorChar)); return PathTools.IsInsideDirectory(dataDirectory, path) ? path : string.Empty; } }