using System; using System.Collections.Generic; using System.Linq; namespace Unity.VisualScripting.FullSerializer { /// <summary> /// The result of some sort of operation. A result is either successful or /// not, but if it is successful then there may be a set of warnings/messages /// associated with it. These warnings describe the performed error recovery /// operations. /// </summary> public struct fsResult { // We cache the empty string array so we can unify some collections // processing code. private static readonly string[] EmptyStringArray = { }; /// <summary> /// Is this result successful? /// </summary> /// <remarks> /// This is intentionally a `success` state so that when the object is /// default constructed it defaults to a failure state. /// </remarks> private bool _success; /// <summary> /// The warning or error messages associated with the result. This may be /// null if there are no messages. /// </summary> private List<string> _messages; /// <summary> /// Adds a new message to this result. /// </summary> /// <param name="message"></param> public void AddMessage(string message) { if (_messages == null) { _messages = new List<string>(); } _messages.Add(message); } /// <summary> /// Adds only the messages from the other result into this result, /// ignoring the success/failure status of the other result. /// </summary> public void AddMessages(fsResult result) { if (result._messages == null) { return; } if (_messages == null) { _messages = new List<string>(); } _messages.AddRange(result._messages); } /// <summary> /// Merges the other result into this one. If the other result failed, /// then this one too will have failed. /// </summary> /// <remarks> /// Note that you can use += instead of this method so that you don't /// bury the actual method call that is generating the other fsResult. /// </remarks> public fsResult Merge(fsResult other) { // Copy success over _success = _success && other._success; // Copy messages over if (other._messages != null) { if (_messages == null) { _messages = new List<string>(other._messages); } else { _messages.AddRange(other._messages); } } return this; } /// <summary> /// A successful result. /// </summary> public static fsResult Success = new fsResult { _success = true }; /// <summary> /// Create a result that is successful but contains the given warning /// message. /// </summary> public static fsResult Warn(string warning) { return new fsResult { _success = true, _messages = new List<string> { warning } }; } /// <summary> /// Create a result that failed. /// </summary> public static fsResult Fail(string warning) { return new fsResult { _success = false, _messages = new List<string> { warning } }; } // TODO: how to make sure this is only used as +=? /// <summary> /// Only use this as +=! /// </summary> public static fsResult operator +(fsResult a, fsResult b) { return a.Merge(b); } /// <summary> /// Did this result fail? If so, you can see the reasons why in /// `RawMessages`. /// </summary> public bool Failed => _success == false; /// <summary> /// Was the result a success? Note that even successful operations may /// have warning messages (`RawMessages`) associated with them. /// </summary> public bool Succeeded => _success; /// <summary> /// Does this result have any warnings? This says nothing about if it /// failed or succeeded, just if it has warning messages associated with /// it. /// </summary> public bool HasWarnings => _messages != null && _messages.Any(); /// <summary> /// A simply utility method that will assert that this result is /// successful. If it is not, then an exception is thrown. /// </summary> public fsResult AssertSuccess() { if (Failed) { throw AsException; } return this; } /// <summary> /// A simple utility method that will assert that this result is /// successful and that there are no warning messages. This throws an /// exception if either of those asserts are false. /// </summary> public fsResult AssertSuccessWithoutWarnings() { if (Failed || RawMessages.Any()) { throw AsException; } return this; } /// <summary> /// Utility method to convert the result to an exception. This method is /// only defined is `Failed` returns true. /// </summary> public Exception AsException { get { if (!Failed && !RawMessages.Any()) { throw new Exception("Only a failed result can be converted to an exception"); } return new Exception(FormattedMessages); } } public IEnumerable<string> RawMessages { get { if (_messages != null) { return _messages; } return EmptyStringArray; } } public string FormattedMessages => string.Join(",\n", RawMessages.ToArray()); } }