using System.CommandLine.Binding; using System.Dynamic; using Duplicati.Library.Backend; using Newtonsoft.Json; namespace Duplicati.CommandLine.ServerUtil; /// /// Manages console output, optionally serializing it to JSON. /// /// /// This class captures command execution details, messages, and exceptions, providing flexibility to either output them to the console /// or serialize them into a JSON format based on the parameter. /// public sealed class OutputInterceptor(bool jsonOutput, BindingContext bindingContext) { private readonly DateTimeOffset _timestamp = DateTimeOffset.Now; private readonly List _commandMessages = []; private readonly List _exceptionMessages = []; private string? _command; private bool _success; private readonly Dictionary _extendedProperties = []; public bool JsonOutputMode { get; } = jsonOutput; public int ExitCode { get; set; } /// /// Gets the binding context associated with this interceptor. /// public BindingContext BindingContext { get; } = bindingContext ?? throw new ArgumentNullException(nameof(bindingContext)); /// /// Sets the command string to be intercepted and tracked. /// /// The command string to set. Must not be null. /// Thrown when is null. public void SetCommand(string command) { _command = command ?? throw new ArgumentNullException(nameof(command)); } /// /// Sets the result of the command execution. /// /// A value indicating whether the ** business rule ** was successful. On exception by definition it will be false. public void SetResult(bool success) { _success = success; } /// /// Appends an exception message to the interceptor. /// /// The exception message to append. Ignored if null or empty. /// /// If JSON output is enabled, the message is stored in a list; otherwise, it is written to the console. /// public void AppendExceptionMessage(string? message) { if (string.IsNullOrEmpty(message)) return; if (JsonOutputMode) { _exceptionMessages.Add(message); } else { Console.WriteLine(message); } } public void AppendCustomObject(string keyName, object? customObject) { _extendedProperties.Add(keyName, customObject); } /// /// Appends a console message to the interceptor. /// /// The console message to append. Ignored if null or empty. /// /// If JSON output is enabled, the message is stored in a list; otherwise, it is written to the console. /// public void AppendConsoleMessage(string? message) { if (message == null) return; if (JsonOutputMode && !string.IsNullOrEmpty(message)) _commandMessages.Add(message); else Console.WriteLine(message); } /// /// Serializes the intercepted data into a JSON string if JSON output is enabled. /// /// /// A JSON string containing the intercepted data, or null if JSON output is disabled. /// /// /// The serialized result includes the timestamp, command, success status, messages, and exceptions in a structured format. /// public string? GetSerializedResult() { if (!JsonOutputMode) return null; dynamic result = new ExpandoObject(); result.Timestamp = _timestamp.ToString("O"); result.UnixTimestamp = _timestamp.ToUnixTimeSeconds(); result.Command = _command; result.Success = _success; result.ExitCode = ExitCode; result.Messages = _commandMessages.AsReadOnly(); result.Exceptions = _exceptionMessages.AsReadOnly(); foreach (var kvp in _extendedProperties) ((IDictionary)result)[kvp.Key] = kvp.Value ?? string.Empty; return JsonConvert.SerializeObject(result, Formatting.Indented); } }