using System;
namespace Unity.VisualScripting
{
///
/// Handles an exception if it occurs.
///
[UnitCategory("Control")]
[UnitOrder(17)]
[UnitFooterPorts(ControlOutputs = true)]
public sealed class TryCatch : Unit
{
///
/// The entry point for the try-catch block.
///
[DoNotSerialize]
[PortLabelHidden]
public ControlInput enter { get; private set; }
///
/// The action to attempt.
///
[DoNotSerialize]
public ControlOutput @try { get; private set; }
///
/// The action to execute if an exception is thrown.
///
[DoNotSerialize]
public ControlOutput @catch { get; private set; }
///
/// The action to execute afterwards, regardless of whether there was an exception.
///
[DoNotSerialize]
public ControlOutput @finally { get; private set; }
///
/// The exception that was thrown in the try block.
///
[DoNotSerialize]
public ValueOutput exception { get; private set; }
[Serialize]
[Inspectable, UnitHeaderInspectable]
[TypeFilter(typeof(Exception), Matching = TypesMatching.AssignableToAll)]
[TypeSet(TypeSet.SettingsAssembliesTypes)]
public Type exceptionType { get; set; } = typeof(Exception);
public override bool canDefine => exceptionType != null && typeof(Exception).IsAssignableFrom(exceptionType);
protected override void Definition()
{
enter = ControlInput(nameof(enter), Enter);
@try = ControlOutput(nameof(@try));
@catch = ControlOutput(nameof(@catch));
@finally = ControlOutput(nameof(@finally));
exception = ValueOutput(exceptionType, nameof(exception));
Assignment(enter, exception);
Succession(enter, @try);
Succession(enter, @catch);
Succession(enter, @finally);
}
public ControlOutput Enter(Flow flow)
{
if (flow.isCoroutine)
{
throw new NotSupportedException("Coroutines cannot catch exceptions.");
}
try
{
flow.Invoke(@try);
}
catch (Exception ex)
{
if (exceptionType.IsInstanceOfType(ex))
{
flow.SetValue(exception, ex);
flow.Invoke(@catch);
}
else
{
throw;
}
}
finally
{
flow.Invoke(@finally);
}
return null;
}
}
}