using Autodesk.Revit.DB;
namespace RevitBridge.Tools
{
///
/// Deterministic commit-time failure handling for tool-owned transactions. Without a
/// preprocessor, Revit resolves commit failures interactively: warnings pop the
/// transient toast (and spam the journal), and error-severity failures block the
/// Revit API thread behind the modal resolution dialog until a human clicks — past
/// the bridge's call budget. Attach() deletes warnings (recorded for the tool
/// result) and turns errors into a rollback, so a headless caller always gets an
/// answer instead of a hang.
///
internal sealed class FailureGuard : IFailuresPreprocessor
{
public List Warnings { get; } = new();
public List Errors { get; } = new();
public static FailureGuard Attach(Transaction transaction)
{
var guard = new FailureGuard();
var options = transaction.GetFailureHandlingOptions();
options.SetFailuresPreprocessor(guard);
options.SetClearAfterRollback(true);
transaction.SetFailureHandlingOptions(options);
return guard;
}
public FailureProcessingResult PreprocessFailures(FailuresAccessor accessor)
{
bool hasError = false;
foreach (FailureMessageAccessor failure in accessor.GetFailureMessages())
{
string description;
try
{
description = failure.GetDescriptionText();
}
catch
{
description = "(failure without description)";
}
if (failure.GetSeverity() == FailureSeverity.Warning)
{
Warnings.Add(description);
accessor.DeleteWarning(failure);
}
else
{
hasError = true;
Errors.Add(description);
}
}
return hasError ? FailureProcessingResult.ProceedWithRollBack : FailureProcessingResult.Continue;
}
/// Formats the recorded errors for a commit-failure message.
public string DescribeErrors()
=> Errors.Count > 0 ? $" Revit failure(s): {string.Join("; ", Errors)}." : string.Empty;
}
}