DocumentPipeline runs a sequence of operations atomically. Any failure rolls back the document to the exact state it was in before the pipeline started — no partial mutations.
| Navigation: Overview | Operations | Examples | Serialization |
using TextAPI.Operations;
using TextAPI.Operations.Pipeline;
var pipeline = new DocumentPipeline(doc);
pipeline
.Add(new ReplaceAllOperation { Find = "TODO", Replace = "DONE" })
.Add(new TrimTrailingWhitespaceOperation())
.Add(new SortLinesOperation());
PipelineResult result = pipeline.Execute();
var result = new DocumentPipeline(doc)
.Add(new PrependOperation { Text = "// generated\n" })
.Add(new ReplaceAllOperation { Find = "old", Replace = "new" })
.Add(new AppendOperation { Text = "\n// end" })
.Execute();
var ops = OperationMapper.FromPipelineJson(jsonString);
pipeline.Add(ops); // IEnumerable<IDocumentOperation>
| Property | Type | Description |
|---|---|---|
Success |
bool |
True if all operations succeeded. |
ErrorMessage |
string? |
Error message from the failed operation. |
FailedOperationIndex |
int |
0-based index of the failing operation. -1 on success. |
WasRolledBack |
bool |
True if the document was restored to its pre-pipeline state. |
TextBefore |
string |
Snapshot of the document before the pipeline ran. |
TextAfter |
string |
Document text after the pipeline (equals TextBefore if rolled back). |
OperationResults |
IReadOnlyList<OperationResult> |
Per-operation results (only for executed operations). |
AuditLog |
AuditLog |
Structured audit trail. See below. |
if (!result.Success)
{
Console.WriteLine($"Failed at op #{result.FailedOperationIndex}: {result.ErrorMessage}");
Console.WriteLine($"Rolled back: {result.WasRolledBack}");
// document is guaranteed to be at its pre-pipeline state
}
Every pipeline execution records a structured log of what happened:
AuditLog log = result.AuditLog;
Console.WriteLine(log.Summary); // "3 op(s) succeeded; +14/-3 chars"
Console.WriteLine(log.AllSucceeded); // true/false
Console.WriteLine(log.TotalCharsInserted); // total insertions across all ops
Console.WriteLine(log.TotalCharsDeleted); // total deletions
foreach (AuditEntry entry in log.Entries)
{
Console.WriteLine($"[{entry.TimestampUtc:HH:mm:ss.fff}] {entry.OperationType}");
Console.WriteLine($" success={entry.Success} +{entry.CharsInserted}/-{entry.CharsDeleted}");
if (entry.MatchCount > 0) Console.WriteLine($" matches={entry.MatchCount}");
if (entry.ErrorMessage != null) Console.WriteLine($" error: {entry.ErrorMessage}");
}
When multiple offset-based operations run in sequence, earlier inserts/deletes shift the positions of later ones. The pipeline handles this automatically:
// These two INSERT_AT operations work correctly in a pipeline:
// Op 1 inserts 6 chars at offset 0 → shifts all offsets by +6
// Op 2's offset is automatically adjusted from 10 → 16
new DocumentPipeline(doc)
.Add(new InsertAtOperation(0, "START ")) // +6 chars drift
.Add(new InsertAtOperation(10, " END")) // auto-adjusted to offset 16
.Execute();
Drift correction applies only to operations implementing IOffsetAwareOperation (the three offset-based ops). Anchor-based operations re-resolve their anchors at execution time and are naturally drift-immune.
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));
var result = pipeline.Execute(cts.Token);
// If cancelled mid-run, document is rolled back and result.Success = false
// result.ErrorMessage = "Pipeline cancelled"
The rollback mechanism is simple and reliable:
snapshot = doc.GetText() is captured.Success=false, throws an exception, or is cancelled), doc.Load(snapshot) is called immediately.Because rollback uses doc.Load(), it resets the undo stack. The pre-pipeline state cannot be recovered via Undo(). If you need the pre-pipeline state preserved in undo history, snapshot it yourself before calling Execute().