using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; using NLog; namespace StabilityMatrix.Core.Processes; /// /// Defines a custom APC message, embeddable in a subprocess output stream. /// Format is as such: /// "{APC}{CustomPrefix}(JsonSerialized ApcMessage){StChar}" /// "\u009f[SM;{"type":"input","data":"hello"}\u009c" /// public readonly record struct ApcMessage { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); public const char ApcChar = (char) 0x9F; public const char StChar = (char) 0x9C; public const string CustomPrefix = "[SM;"; [JsonPropertyName("type")] public required ApcType Type { get; init; } [JsonPropertyName("data")] public required string Data { get; init; } /// /// Attempts to extract an APC message from the given text /// /// ApcMessage struct public static bool TryParse(string value, [NotNullWhen(true)] out ApcMessage? message) { message = null; var startIndex = value.IndexOf(ApcChar); if (startIndex == -1) return false; // Check the IdPrefix follows the ApcEscape var idIndex = value.IndexOf(CustomPrefix, startIndex + 1, StringComparison.Ordinal); if (idIndex == -1) return false; // Get the end index (ST escape) var stIndex = value.IndexOf(StChar, idIndex + CustomPrefix.Length); if (stIndex == -1) return false; // Extract the json string (between idIndex and stIndex) var json = value.Substring(idIndex + CustomPrefix.Length, stIndex - idIndex - CustomPrefix.Length); try { message = JsonSerializer.Deserialize(json); return true; } catch (Exception e) { Logger.Warn($"Failed to deserialize APC message: {e.Message}"); return false; } } }