using System.IO.Compression; using System.Text.Json; using System.Text.Json.Serialization; namespace Compiler; public class NubLib { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true, }; public static void Pack(string outputPath, string archivePath, Manifest manifest) { using var fs = new FileStream(outputPath, FileMode.Create); using var zip = new ZipArchive(fs, ZipArchiveMode.Create); var manifestEntry = zip.CreateEntry("manifest.json"); using (var writer = new StreamWriter(manifestEntry.Open())) { var serialized = JsonSerializer.Serialize(manifest, JsonOptions); writer.Write(serialized); } var archiveEntry = zip.CreateEntry("lib.a"); using var entryStream = archiveEntry.Open(); using var fileStream = File.OpenRead(archivePath); fileStream.CopyTo(entryStream); } public static NubLibLoadResult Unpack(string nublibPath) { using var fs = new FileStream(nublibPath, FileMode.Open, FileAccess.Read); using var zip = new ZipArchive(fs, ZipArchiveMode.Read); var manifestEntry = zip.GetEntry("manifest.json") ?? throw new FileNotFoundException("Manifest not found in nublib", "manifest.json"); Manifest manifest; using (var reader = new StreamReader(manifestEntry.Open())) { var json = reader.ReadToEnd(); manifest = JsonSerializer.Deserialize(json, JsonOptions) ?? throw new InvalidDataException("Failed to deserialize manifest.json"); } var archiveEntry = zip.Entries.FirstOrDefault(e => e.Name.EndsWith(".a")) ?? throw new FileNotFoundException("Archive not found in nublib", "*.a"); string tempArchivePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".a"); using (var entryStream = archiveEntry.Open()) using (var tempFile = File.Create(tempArchivePath)) { entryStream.CopyTo(tempFile); } return new NubLibLoadResult(manifest, tempArchivePath); } public record NubLibLoadResult(Manifest Manifest, string ArchivePath); } public record Manifest(Dictionary Modules) { public static Manifest Create(ModuleGraph moduleGraph) { var modules = new Dictionary(); foreach (var module in moduleGraph.GetModules()) { var types = module.GetTypes().ToDictionary(x => x.Key, x => ConvertType(x.Value)); var identifiers = module.GetIdentifiers().ToDictionary(x => x.Key, x => new Module.IdentifierInfo(x.Value.Exported, x.Value.Type)); modules[module.Name] = new Module(types, identifiers); } return new Manifest(modules); static Module.TypeInfo ConvertType(Compiler.Module.TypeInfo typeInfo) { return typeInfo switch { Compiler.Module.TypeInfoStruct s => new Module.TypeInfoStruct(s.Exported, s.Packed, s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList()), _ => throw new ArgumentOutOfRangeException(nameof(typeInfo)) }; } } public record Module(Dictionary Types, Dictionary Identifiers) { public record IdentifierInfo(bool Exported, NubType Type); [JsonDerivedType(typeof(TypeInfoStruct), "struct")] public abstract record TypeInfo(bool Exported); public record TypeInfoStruct(bool Exported, bool Packed, IReadOnlyList Fields) : TypeInfo(Exported) { public record Field(string Name, NubType Type); } } }