using System.Text; using Microsoft.Extensions.FileProviders; #if RELEASE using System.Reflection; #endif namespace AIStudio.Tools; /// /// Reads files which come with AI Studio, such as prompts, guides, or the built-in plugins. /// /// /// The files lie below the project folder and are listed as EmbeddedResource in the project file. A development /// build reads them from the disk, so that a change shows without building again; a release reads them from the /// resources embedded into the app. Paths are relative to the project folder and use forward slashes. /// public static class AppResources { /// /// Gets a file provider for a folder of the app. /// /// The folder, relative to the project folder, e.g., "Plugins". /// The file provider, whose paths are relative to that folder. public static IFileProvider FolderProvider(string relativeFolder) { var folder = Normalize(relativeFolder); #if DEBUG return new PhysicalFileProvider(Path.Join(Environment.CurrentDirectory, folder)); #else return new ManifestEmbeddedFileProvider(Assembly.GetAssembly(typeof(Program))!, folder); #endif } /// /// Reads a text file of the app. /// /// The file, relative to the project folder, e.g., "Assistants/PromptOptimizer/prompting_guideline.md". /// The cancellation token. /// The text, or an empty string when the app has no such file. public static async Task ReadTextAsync(string relativePath, CancellationToken token = default) { var path = Normalize(relativePath); #if DEBUG var filePath = Path.Join(Environment.CurrentDirectory, path); return File.Exists(filePath) ? await File.ReadAllTextAsync(filePath, Encoding.UTF8, token) : string.Empty; #else var file = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(typeof(Program))!).GetFileInfo(path); if (!file.Exists || file.IsDirectory) return string.Empty; await using var stream = file.CreateReadStream(); using var reader = new StreamReader(stream, Encoding.UTF8); return await reader.ReadToEndAsync(token); #endif } /// /// Turns a path into the form both kinds of builds understand: forward slashes, without a slash at either end. /// /// The path. private static string Normalize(string relativePath) => relativePath.Replace('\\', '/').Trim('/'); }