using System;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.VisualScripting
{
public static class GraphsExceptionUtility
{
// Note: Checking hasDebugData here instead of enableDebug,
// because we always want exceptions to register, even if
// background debug is disabled.
private const string handledKey = "Bolt.Core.Handled";
public static Exception GetException(this IGraphElementWithDebugData element, GraphPointer pointer)
{
if (!pointer.hasDebugData)
{
return null;
}
var debugData = pointer.GetElementDebugData<IGraphElementDebugData>(element);
return debugData.runtimeException;
}
public static void SetException(this IGraphElementWithDebugData element, GraphPointer pointer, Exception ex)
{
if (!pointer.hasDebugData)
{
return;
}
var debugData = pointer.GetElementDebugData<IGraphElementDebugData>(element);
debugData.runtimeException = ex;
}
public static void HandleException(this IGraphElementWithDebugData element, GraphPointer pointer, Exception ex)
{
Ensure.That(nameof(ex)).IsNotNull(ex);
if (pointer == null)
{
Debug.LogError("Caught exception with null graph pointer (flow was likely disposed):\n" + ex);
return;
}
var reference = pointer.AsReference();
if (!ex.HandledIn(reference))
{
element.SetException(pointer, ex);
}
while (reference.isChild)
{
var parentElement = reference.parentElement;
reference = reference.ParentReference(true);
if (parentElement is IGraphElementWithDebugData debuggableParentElement)
{
if (!ex.HandledIn(reference))
{
debuggableParentElement.SetException(reference, ex);
}
}
}
}
private static bool HandledIn(this Exception ex, GraphReference reference)
{
Ensure.That(nameof(ex)).IsNotNull(ex);
if (!ex.Data.Contains(handledKey))
{
ex.Data.Add(handledKey, new HashSet<GraphReference>());
}
var handled = (HashSet<GraphReference>)ex.Data[handledKey];
if (handled.Contains(reference))
{
return true;
}
else
{
handled.Add(reference);
return false;
}
}
}
}