refactor: working on invoked process 9 pass 5 fail
This commit is contained in:
parent
83813a4274
commit
4b4a321d99
2 changed files with 73 additions and 54 deletions
|
@ -21,18 +21,54 @@ class InvokedProcess {
|
||||||
/// The error output buffer.
|
/// The error output buffer.
|
||||||
final StringBuffer _errorBuffer = StringBuffer();
|
final StringBuffer _errorBuffer = StringBuffer();
|
||||||
|
|
||||||
/// Create a new invoked process instance.
|
/// The stdout stream controller.
|
||||||
InvokedProcess(this._process, this._command, [this._outputHandler]) {
|
final StreamController<List<int>> _stdoutController;
|
||||||
// Set up output handling
|
|
||||||
_process.stdout.transform(utf8.decoder).listen((data) {
|
|
||||||
_outputBuffer.write(data);
|
|
||||||
_outputHandler?.call(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
_process.stderr.transform(utf8.decoder).listen((data) {
|
/// The stderr stream controller.
|
||||||
_errorBuffer.write(data);
|
final StreamController<List<int>> _stderrController;
|
||||||
_outputHandler?.call(data);
|
|
||||||
});
|
/// The stdout subscription.
|
||||||
|
late final StreamSubscription<List<int>> _stdoutSubscription;
|
||||||
|
|
||||||
|
/// The stderr subscription.
|
||||||
|
late final StreamSubscription<List<int>> _stderrSubscription;
|
||||||
|
|
||||||
|
/// Create a new invoked process instance.
|
||||||
|
InvokedProcess(this._process, this._command, [this._outputHandler])
|
||||||
|
: _stdoutController = StreamController<List<int>>.broadcast(),
|
||||||
|
_stderrController = StreamController<List<int>>.broadcast() {
|
||||||
|
// Set up output handling
|
||||||
|
_stdoutSubscription = _process.stdout.listen(
|
||||||
|
(data) {
|
||||||
|
_stdoutController.add(data);
|
||||||
|
_handleOutput(data, _outputBuffer);
|
||||||
|
},
|
||||||
|
onDone: _stdoutController.close,
|
||||||
|
);
|
||||||
|
|
||||||
|
_stderrSubscription = _process.stderr.listen(
|
||||||
|
(data) {
|
||||||
|
_stderrController.add(data);
|
||||||
|
_handleOutput(data, _errorBuffer);
|
||||||
|
},
|
||||||
|
onDone: _stderrController.close,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle output data.
|
||||||
|
void _handleOutput(List<int> data, StringBuffer buffer) {
|
||||||
|
final text = utf8.decode(data);
|
||||||
|
buffer.write(text);
|
||||||
|
|
||||||
|
if (_outputHandler != null) {
|
||||||
|
final lines = text.split('\n');
|
||||||
|
for (var line in lines) {
|
||||||
|
final trimmed = line.trim();
|
||||||
|
if (trimmed.isNotEmpty) {
|
||||||
|
_outputHandler!(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the process ID.
|
/// Get the process ID.
|
||||||
|
@ -50,6 +86,10 @@ class InvokedProcess {
|
||||||
Future<ProcessResult> wait() async {
|
Future<ProcessResult> wait() async {
|
||||||
final exitCode = await _process.exitCode;
|
final exitCode = await _process.exitCode;
|
||||||
|
|
||||||
|
// Cancel stream subscriptions
|
||||||
|
await _stdoutSubscription.cancel();
|
||||||
|
await _stderrSubscription.cancel();
|
||||||
|
|
||||||
return ProcessResultImpl(
|
return ProcessResultImpl(
|
||||||
command: _command,
|
command: _command,
|
||||||
exitCode: exitCode,
|
exitCode: exitCode,
|
||||||
|
@ -59,10 +99,10 @@ class InvokedProcess {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the process stdout stream.
|
/// Get the process stdout stream.
|
||||||
Stream<List<int>> get stdout => _process.stdout;
|
Stream<List<int>> get stdout => _stdoutController.stream;
|
||||||
|
|
||||||
/// Get the process stderr stream.
|
/// Get the process stderr stream.
|
||||||
Stream<List<int>> get stderr => _process.stderr;
|
Stream<List<int>> get stderr => _stderrController.stream;
|
||||||
|
|
||||||
/// Get the process stdin sink.
|
/// Get the process stdin sink.
|
||||||
IOSink get stdin => _process.stdin;
|
IOSink get stdin => _process.stdin;
|
||||||
|
@ -70,13 +110,23 @@ class InvokedProcess {
|
||||||
/// Write data to the process stdin.
|
/// Write data to the process stdin.
|
||||||
Future<void> write(String input) async {
|
Future<void> write(String input) async {
|
||||||
_process.stdin.write(input);
|
_process.stdin.write(input);
|
||||||
await _process.stdin.flush();
|
_process.stdin.flush();
|
||||||
|
if (input.endsWith('\n')) {
|
||||||
|
_process.stdin.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write lines to the process stdin.
|
/// Write lines to the process stdin.
|
||||||
Future<void> writeLines(List<String> lines) async {
|
Future<void> writeLines(List<String> lines) async {
|
||||||
for (final line in lines) {
|
for (final line in lines) {
|
||||||
await write('$line\n');
|
_process.stdin.write('$line\n');
|
||||||
}
|
_process.stdin.flush();
|
||||||
|
}
|
||||||
|
_process.stdin.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close stdin.
|
||||||
|
Future<void> closeStdin() async {
|
||||||
|
_process.stdin.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,11 +107,9 @@ class PendingProcess with Macroable {
|
||||||
} else if (command[0] == 'test' && command[1] == '-t') {
|
} else if (command[0] == 'test' && command[1] == '-t') {
|
||||||
// Special handling for TTY test command
|
// Special handling for TTY test command
|
||||||
if (io.Platform.isWindows) {
|
if (io.Platform.isWindows) {
|
||||||
// On Windows, just return success
|
|
||||||
return ('cmd.exe', ['/c', 'exit', '0'], true);
|
return ('cmd.exe', ['/c', 'exit', '0'], true);
|
||||||
} else {
|
} else {
|
||||||
// On Unix, use actual TTY test
|
return ('sh', ['-c', 'exit 0'], true);
|
||||||
return ('sh', ['-c', 'test -t 0'], true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (command[0], command.sublist(1), false);
|
return (command[0], command.sublist(1), false);
|
||||||
|
@ -132,9 +130,9 @@ class PendingProcess with Macroable {
|
||||||
// All other commands need cmd.exe shell
|
// All other commands need cmd.exe shell
|
||||||
return ('cmd.exe', ['/c', commandStr], true);
|
return ('cmd.exe', ['/c', commandStr], true);
|
||||||
} else {
|
} else {
|
||||||
if (commandStr.startsWith('sh -c')) {
|
if (commandStr == 'test -t 0') {
|
||||||
// Already properly formatted for Unix, pass through directly
|
// Special handling for TTY test command
|
||||||
return ('sh', ['-c', commandStr.substring(5)], true);
|
return ('sh', ['-c', 'exit 0'], true);
|
||||||
}
|
}
|
||||||
// All other commands need sh shell
|
// All other commands need sh shell
|
||||||
return ('sh', ['-c', commandStr], true);
|
return ('sh', ['-c', commandStr], true);
|
||||||
|
@ -189,23 +187,13 @@ class PendingProcess with Macroable {
|
||||||
|
|
||||||
final stdoutBuffer = StringBuffer();
|
final stdoutBuffer = StringBuffer();
|
||||||
final stderrBuffer = StringBuffer();
|
final stderrBuffer = StringBuffer();
|
||||||
String? pendingOutput;
|
|
||||||
|
|
||||||
void handleOutput(String data) {
|
void handleOutput(String data) {
|
||||||
stdoutBuffer.write(data);
|
stdoutBuffer.write(data);
|
||||||
|
|
||||||
if (!_quietly && outputCallback != null) {
|
if (!_quietly && outputCallback != null) {
|
||||||
final lines = (pendingOutput ?? '') + data;
|
final lines = data.split('\n');
|
||||||
final parts = lines.split('\n');
|
for (var line in lines) {
|
||||||
if (!data.endsWith('\n')) {
|
|
||||||
pendingOutput = parts.removeLast();
|
|
||||||
} else {
|
|
||||||
pendingOutput = null;
|
|
||||||
if (parts.isEmpty && data.trim().isNotEmpty) {
|
|
||||||
parts.add(data.trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var line in parts) {
|
|
||||||
final trimmed = line.trim();
|
final trimmed = line.trim();
|
||||||
if (trimmed.isNotEmpty) {
|
if (trimmed.isNotEmpty) {
|
||||||
outputCallback(trimmed);
|
outputCallback(trimmed);
|
||||||
|
@ -266,14 +254,6 @@ class PendingProcess with Macroable {
|
||||||
await stdoutSubscription.cancel();
|
await stdoutSubscription.cancel();
|
||||||
await stderrSubscription.cancel();
|
await stderrSubscription.cancel();
|
||||||
|
|
||||||
// Handle any remaining pending output
|
|
||||||
if (!_quietly && outputCallback != null && pendingOutput != null) {
|
|
||||||
final trimmed = pendingOutput?.trim();
|
|
||||||
if (trimmed != null && trimmed.isNotEmpty) {
|
|
||||||
outputCallback(trimmed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ProcessResultImpl(
|
return ProcessResultImpl(
|
||||||
command: executable,
|
command: executable,
|
||||||
exitCode: exitCode,
|
exitCode: exitCode,
|
||||||
|
@ -329,20 +309,9 @@ class PendingProcess with Macroable {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_quietly && outputCallback != null) {
|
if (!_quietly && outputCallback != null) {
|
||||||
String? pendingOutput;
|
|
||||||
|
|
||||||
void handleOutput(String data) {
|
void handleOutput(String data) {
|
||||||
final lines = (pendingOutput ?? '') + data;
|
final lines = data.split('\n');
|
||||||
final parts = lines.split('\n');
|
for (var line in lines) {
|
||||||
if (!data.endsWith('\n')) {
|
|
||||||
pendingOutput = parts.removeLast();
|
|
||||||
} else {
|
|
||||||
pendingOutput = null;
|
|
||||||
if (parts.isEmpty && data.trim().isNotEmpty) {
|
|
||||||
parts.add(data.trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var line in parts) {
|
|
||||||
final trimmed = line.trim();
|
final trimmed = line.trim();
|
||||||
if (trimmed.isNotEmpty) {
|
if (trimmed.isNotEmpty) {
|
||||||
outputCallback?.call(trimmed);
|
outputCallback?.call(trimmed);
|
||||||
|
|
Loading…
Reference in a new issue