using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Tomlyn; using Tomlyn.Model; namespace SessionZero.Data.Sztl; public static class SztlParser { public static SzTemplate ParseTemplate(string tomlText) { var doc = Toml.ToModel(tomlText); if (!doc.ContainsKey("metadata") || doc["metadata"] is not TomlTable metadata) { throw new Exception("Template missing the [metadata] section"); } // Determine template type and fill metadata string templateType = metadata.TryGetValue("template_type", out var tempTypeVal) ? tempTypeVal?.ToString() ?? throw new Exception("Template missing template_type metadata") : throw new Exception("Template missing template_type metadata"); SzTemplate template = templateType switch { "data" => new SzDataObjectTemplate(), _ => throw new NotImplementedException($"Unknown template_type '{templateType}'") }; template.SztlText = tomlText.Trim(); if (metadata.TryGetValue("id", out var idVal)) template.Id = idVal?.ToString()!; else throw new Exception("Template missing id metadata"); if (metadata.TryGetValue("uuid", out var uuidVal)) template.Uuid = uuidVal?.ToString()!; else throw new Exception("Template missing uuid metadata"); if (metadata.TryGetValue("version", out var versionVal)) template.Version = versionVal?.ToString()!; else throw new Exception("Template missing version metadata"); if (metadata.TryGetValue("compatible_systems", out var compSysVal) && compSysVal is string[] arr) template.CompatibleSystems = arr.ToList(); if (metadata.TryGetValue("description", out var descVal)) template.Description = descVal?.ToString()!; else throw new Exception("Template missing description metadata"); template.ParseAdditionalMetaData(doc); // Process all other tables as fields or groups ProcessTable(template, doc); return template; } private static void InsertFieldIntoGroup(SzTemplate template, string[] pathParts, TomlTable table) { var groupDict = template.SubGroups; SztlFieldGroup currentGroup = null!; for (int i = 0; i < pathParts.Length - 1; i++) { var part = pathParts[i]; if (!groupDict.TryGetValue(part, out var grp)) { grp = new SztlFieldGroup { Id = part }; groupDict[part] = grp; } currentGroup = grp; groupDict = grp.SubGroups; } var fieldId = pathParts[^1]; var leafTable = table; if (table.TryGetValue(fieldId, out var possibleLeaf) && possibleLeaf is TomlTable t) { leafTable = t; } var field = ParseFieldFromTable(fieldId, leafTable); currentGroup.Fields[fieldId] = field; } private static SztlField ParseFieldFromTable(string id, TomlTable table) { var field = new SztlField { Id = id }; if (!table.TryGetValue("type", out var tVal)) { throw new Exception($"Field '{id}' missing type"); } var typeStr = tVal?.ToString() ?? throw new Exception($"Field '{id}' has null type"); if (!Enum.TryParse(typeStr, ignoreCase: true, out var ftype)) { throw new Exception($"Field '{id}' has unknown type '{typeStr}'"); } field.Type = ftype; if (table.TryGetValue("default_value", out var dVal)) { field.DefaultValue = dVal; } if (ftype == SzFieldType.Textblock) { field.IsTextBlock = true; } return field; } private static void ProcessTable(SzTemplate template, TomlTable table, string parentPath = "") { foreach (var (key, value) in table) { if (key == "metadata") continue; var path = string.IsNullOrEmpty(parentPath) ? key : $"{parentPath}.{key}"; if (value is TomlTable t) { if (t.ContainsKey("type")) { var field = ParseFieldFromTable(key, t); if (string.IsNullOrEmpty(parentPath)) { template.Fields[key] = field; } else { InsertFieldIntoGroup(template, path.Split('.'), t); } } else { ProcessTable(template, t, path); } } } } }