using Ghost.Core; using System.Text.Json; namespace Ghost.Editor.Core.AssetHandle; public static partial class AssetDatabase { /// /// Get the relative path from the assets directory. /// private static Result GetRelativePath(string fullPath) { if (AssetsDirectory == null) { return Result.Failure("AssetsDirectory not initialized"); } if (!fullPath.StartsWith(AssetsDirectory.FullName, StringComparison.OrdinalIgnoreCase)) { return Result.Failure("Path is not within assets directory"); } return Path.GetRelativePath(AssetsDirectory.FullName, fullPath); } /// /// Get the full path from a relative path. /// private static Result GetFullPath(string relativePath) { if (AssetsDirectory == null) { return Result.Failure("AssetsDirectory not initialized"); } return Path.Combine(AssetsDirectory.FullName, relativePath); } /// /// Find GUID by asset path. /// /// Full or relative path to the asset. /// The GUID of the asset if found. public static Result PathToGuid(string assetPath) { var relativePath = assetPath; // Convert to relative path if it's a full path if (Path.IsPathRooted(assetPath)) { var relResult = GetRelativePath(assetPath); if (relResult.IsFailure) { return Result.Failure(relResult.Message); } relativePath = relResult.Value; } // Normalize path separators relativePath = relativePath.Replace('\\', '/'); lock (s_dbLock) { if (s_pathAssetLookup.TryGetValue(relativePath, out var guid)) { return guid; } } return Result.Failure("Asset not found in database"); } /// /// Find path by GUID. /// /// GUID of the asset. /// The relative path to the asset if found. public static Result GuidToPath(Guid guid) { lock (s_dbLock) { if (s_assetPathLookup.TryGetValue(guid, out var path)) { return path; } } return Result.Failure("Asset GUID not found in database"); } /// /// Load asset by GUID with caching. /// /// Type of asset to load. /// GUID of the asset. /// The loaded asset. public static Result LoadAsset(Guid guid) where T : Asset { // Implemented in AssetDatabase.Loader.cs return LoadAssetInternal(guid); } /// /// Get asset tags by GUID. /// /// GUID of the asset. /// List of tags associated with the asset. public static async ValueTask>> GetAssetTagsAsync(Guid guid, CancellationToken token = default) { var pathResult = GuidToPath(guid); if (pathResult.IsFailure) { return Result>.Failure(pathResult.Message); } var fullPathResult = GetFullPath(pathResult.Value); if (fullPathResult.IsFailure) { return Result>.Failure(fullPathResult.Message); } var metaResult = await ReadMetaFileAsync(fullPathResult.Value, token); if (metaResult.IsFailure) { return Result>.Failure(metaResult.Message); } return metaResult.Value.Tags; } /// /// Set asset tags by GUID. /// /// GUID of the asset. /// New tags for the asset. /// Result indicating success or failure. public static async ValueTask SetAssetTagsAsync(Guid guid, List tags, CancellationToken token = default) { var pathResult = GuidToPath(guid); if (pathResult.IsFailure) { return Result.Failure(pathResult.Message); } var fullPathResult = GetFullPath(pathResult.Value); if (fullPathResult.IsFailure) { return Result.Failure(fullPathResult.Message); } var metaResult = await ReadMetaFileAsync(fullPathResult.Value, token); if (metaResult.IsFailure) { return Result.Failure(metaResult.Message); } metaResult.Value.Tags = tags; // Write updated metadata to .gmeta file var writeResult = await WriteMetaFileAsync(fullPathResult.Value + Utilities.FileExtensions.META_FILE_EXTENSION, metaResult.Value, token); if (writeResult.IsFailure) { return writeResult; } // Update database with new tags var fileHash = await CalculateFileHashAsync(fullPathResult.Value, token); return await UpsertAssetAsync(fullPathResult.Value, metaResult.Value, fileHash, null, token); } /// /// Search assets by name pattern. /// Supports SQL LIKE wildcards: * (any characters) and ? (single character). /// /// Search pattern (e.g., "*.txt", "player?", "test*"). /// List of matching asset GUIDs. public static async Task> FindAssetsByNameAsync(string namePattern, CancellationToken token = default) { return await GetAssetsByNameAsync(namePattern, token); } /// /// Find assets by tag. /// /// Tag to search for. /// List of asset GUIDs with the specified tag. public static async Task> FindAssetsByTagAsync(string tag, CancellationToken token = default) { return await GetAssetsByTagAsync(tag, token); } /// /// Get all assets in the database. /// /// Dictionary mapping GUIDs to relative paths. public static IReadOnlyDictionary GetAllAssets() { lock (s_dbLock) { return s_assetPathLookup.AsReadOnly(); } } }