using SessionZero.SzfLib.File; using SessionZero.SzfLib.Helpers; using SessionZero.SzfLib.Objects; namespace SessionZero.SzfLib.Parser; public class SzfParser : ISzfParser { /// /// Tries to parse an ISzfObject /// /// /// /// public ISzfObject? Parse(string szfContent) { SzfError result = new(); var lines = szfContent.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string szfObjectTypeName = string.Empty; Type? szfObjectType = null; string szfVersion = "1.0.0"; var sections = new Dictionary(); for (int idx = 0; idx < lines.Length; idx++) { var line = lines[idx].Trim(); if (line.StartsWith("#") || string.IsNullOrWhiteSpace(line)) continue; if (line.StartsWith("!type:", StringComparison.OrdinalIgnoreCase)) { var typeIdentifier = line.Substring("!type:".Length).Trim(); szfObjectTypeName = typeIdentifier; szfObjectType = SzfHelper.FindSzfObjectTypeByAttributeType(typeIdentifier); continue; } if (line.StartsWith("!schema:", StringComparison.OrdinalIgnoreCase)) { var version = line.Substring("!schema:".Length).Trim(); if (!string.IsNullOrEmpty(version)) szfVersion = version; continue; } if (line.StartsWith("[") && line.EndsWith("]")) { var sectionName = line.Substring(1, line.Length - 2).Trim(); var sectionFields = new List(); int i = idx + 1; while (i < lines.Length && !(lines[i].Trim().StartsWith("[") && lines[i].Trim().EndsWith("]"))) { var fieldLine = lines[i].Trim(); if (!string.IsNullOrWhiteSpace(fieldLine) && !fieldLine.StartsWith("#")) sectionFields.Add(fieldLine); i++; } idx = i - 1; CreateSection(sectionName, sectionFields, sections); } } if (szfObjectType != null && sections.Count > 0) { var szfObject = (ISzfObject)Activator.CreateInstance(szfObjectType)!; szfObject.SzfType = szfObjectTypeName; szfObject.SzfVersion = szfVersion; // Only add root sections (those without a dot in their name) foreach (var section in sections.Values.Where(s => !sections.Keys.Any(k => k.EndsWith("." + s.Name)))) { szfObject.Sections.Add(section); } result = szfObject.Validate(); if (result.IsValid) return szfObject; } throw result; } public ISzfObject? Parse(ISzfFile file) { if (file == null) { throw new ArgumentNullException(nameof(file), "File cannot be null."); } if (string.IsNullOrEmpty(file.Content)) { throw new ArgumentException("File content cannot be null or empty.", nameof(file.Content)); } return Parse(file.Content); } private SzfSection CreateSection(string sectionPath, IEnumerable fields, Dictionary sections) { var nameParts = sectionPath.Split('.'); SzfSection? parent = null; string currentPath = ""; for (int p = 0; p < nameParts.Length; p++) { currentPath = currentPath == "" ? nameParts[p] : currentPath + "." + nameParts[p]; if (!sections.TryGetValue(currentPath, out var section)) { section = new SzfSection(nameParts[p]); sections[currentPath] = section; if (parent != null) parent.Subsections[nameParts[p]] = section; } parent = section; } foreach (var field in fields) { var szfField = CreateField(field); parent!.Fields.Add(szfField.Name, szfField); } return parent!; } private SzfField CreateField(string field) { // Expected format: FieldName (type) = Value int typeStart = field.IndexOf('('); int typeEnd = field.IndexOf(')'); if (typeStart < 0 || typeEnd < 0 || typeEnd < typeStart) throw new FormatException($"Invalid field format: {field}"); string fieldName = field.Substring(0, typeStart).Trim(); string fieldType = field.Substring(typeStart + 1, typeEnd - typeStart - 1).Trim().Replace("-", string.Empty); // Find the first '=' after the closing parenthesis int eqIndex = field.IndexOf('=', typeEnd + 1); string fieldValue = eqIndex >= 0 ? field.Substring(eqIndex + 1).Trim() : string.Empty; if (!Enum.TryParse(fieldType, true, out SzfFieldType type)) throw new FormatException($"Invalid field type: {fieldType}"); SzfField szfField = new(fieldName, type) { Value = fieldValue }; return szfField; } }