|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Microsoft.DotNet.Cli.Utils;
public sealed class StreamForwarder
{
private static readonly char[] s_ignoreCharacters = ['\r'];
private static readonly char s_flushBuilderCharacter = '\n';
private StringBuilder? _builder;
private StringWriter? _capture;
private Action<string>? _writeLine;
private bool _trimTrailingCapturedNewline;
public string? CapturedOutput
{
get
{
var capture = _capture?.GetStringBuilder()?.ToString();
if (_trimTrailingCapturedNewline)
{
capture = capture?.TrimEnd('\r', '\n');
}
return capture;
}
}
public StreamForwarder Capture(bool trimTrailingNewline = false)
{
ThrowIfCaptureSet();
_capture = new StringWriter();
_trimTrailingCapturedNewline = trimTrailingNewline;
return this;
}
public StreamForwarder ForwardTo(Action<string> writeLine)
{
ThrowIfNull(writeLine);
ThrowIfForwarderSet();
_writeLine = writeLine;
return this;
}
public Task BeginRead(TextReader reader)
{
return Task.Run(() => Read(reader));
}
public void Read(TextReader reader)
{
var bufferSize = 1;
int readCharacterCount;
char currentCharacter;
var buffer = new char[bufferSize];
_builder = new StringBuilder();
// Using Read with buffer size 1 to prevent looping endlessly
// like we would when using Read() with no buffer
while ((readCharacterCount = reader.Read(buffer, 0, bufferSize)) > 0)
{
currentCharacter = buffer[0];
if (currentCharacter == s_flushBuilderCharacter)
{
WriteBuilder();
}
else if (!s_ignoreCharacters.Contains(currentCharacter))
{
_builder.Append(currentCharacter);
}
}
// Flush anything else when the stream is closed
// Which should only happen if someone used console.Write
if (_builder.Length > 0)
{
WriteBuilder();
}
}
private void WriteBuilder()
{
WriteLine(_builder?.ToString() ?? string.Empty);
_builder?.Clear();
}
private void WriteLine(string str)
{
if (_capture != null)
{
_capture.WriteLine(str);
}
_writeLine?.Invoke(str);
}
private void ThrowIfNull(object obj)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
}
private void ThrowIfForwarderSet()
{
if (_writeLine != null)
{
throw new InvalidOperationException(LocalizableStrings.WriteLineForwarderSetPreviously);
}
}
private void ThrowIfCaptureSet()
{
if (_capture != null)
{
throw new InvalidOperationException(LocalizableStrings.AlreadyCapturingStream);
}
}
}
|