pipe_to_file.dart 2.03 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:mojo/core.dart';

// Helper class to drain the contents of a mojo data pipe to a file.
class PipeToFile {
  MojoDataPipeConsumer _consumer;
  MojoEventStream _eventStream;
  IOSink _outputStream;

  PipeToFile(this._consumer, String outputPath) {
    _eventStream = new MojoEventStream(_consumer.handle);
    _outputStream = new File(outputPath).openWrite();
  }

  Future<MojoResult> _doRead() async {
    ByteData thisRead = _consumer.beginRead();
    if (thisRead == null) {
      throw 'Data pipe beginRead failed: ${_consumer.status}';
    }
    // TODO(mpcomplete): Should I worry about the _eventStream listen callback
    // being invoked again before this completes?
    await _outputStream.add(thisRead.buffer.asUint8List());
    return _consumer.endRead(thisRead.lengthInBytes);
  }

  Future<MojoResult> drain() async {
    var completer = new Completer();
    // TODO(mpcomplete): Is it legit to pass an async callback to listen?
    _eventStream.listen((List<int> event) async {
      var mojoSignals = new MojoHandleSignals(event[1]);
      if (mojoSignals.isReadable) {
        var result = await _doRead();
        if (!result.isOk) {
          _eventStream.close();
          _eventStream = null;
          _outputStream.close();
          completer.complete(result);
        } else {
          _eventStream.enableReadEvents();
        }
      } else if (mojoSignals.isPeerClosed) {
        _eventStream.close();
        _eventStream = null;
        _outputStream.close();
        completer.complete(MojoResult.OK);
      } else {
        throw 'Unexpected handle event: $mojoSignals';
      }
    });
    return completer.future;
  }

  static Future<MojoResult> copyToFile(MojoDataPipeConsumer consumer, String outputPath) {
    var drainer = new PipeToFile(consumer, outputPath);
    return drainer.drain();
  }
}